URI:
       t9660srv.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       t9660srv.c (17394B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <auth.h>
            4 #include <fcall.h>
            5 #include "dat.h"
            6 #include "fns.h"
            7 #include "iso9660.h"
            8 
            9 static void        ireset(void);
           10 static int        iattach(Xfile*);
           11 static void        iclone(Xfile*, Xfile*);
           12 static void        iwalkup(Xfile*);
           13 static void        iwalk(Xfile*, char*);
           14 static void        iopen(Xfile*, int);
           15 static void        icreate(Xfile*, char*, long, int);
           16 static long        ireaddir(Xfile*, uchar*, long, long);
           17 static long        iread(Xfile*, char*, vlong, long);
           18 static long        iwrite(Xfile*, char*, vlong, long);
           19 static void        iclunk(Xfile*);
           20 static void        iremove(Xfile*);
           21 static void        istat(Xfile*, Dir*);
           22 static void        iwstat(Xfile*, Dir*);
           23 
           24 static char*        nstr(uchar*, int);
           25 static char*        rdate(uchar*, int);
           26 static int        getcontin(Xdata*, uchar*, uchar**);
           27 static int        getdrec(Xfile*, void*);
           28 static void        ungetdrec(Xfile*);
           29 static int        opendotdot(Xfile*, Xfile*);
           30 static int        showdrec(int, int, void*);
           31 static long        gtime(uchar*);
           32 static long        l16(void*);
           33 static long        l32(void*);
           34 static void        newdrec(Xfile*, Drec*);
           35 static int        rzdir(Xfs*, Dir*, int, Drec*);
           36 
           37 Xfsub        isosub =
           38 {
           39         ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
           40         ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
           41 };
           42 
           43 static void
           44 ireset(void)
           45 {}
           46 
           47 static int
           48 iattach(Xfile *root)
           49 {
           50         Xfs *cd = root->xf;
           51         Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
           52         int fmt, blksize, i, n, l, haveplan9;
           53         Iobuf *dirp;
           54         uchar dbuf[256];
           55         Drec *rd = (Drec *)dbuf;
           56         uchar *q, *s;
           57 
           58         dirp = nil;
           59         blksize = 0;
           60         fmt = 0;
           61         dp = nil;
           62         haveplan9 = 0;
           63         for(i=VOLDESC;i<VOLDESC+100; i++){        /* +100 for sanity */
           64                 p = getbuf(cd->d, i);
           65                 v = (Voldesc*)(p->iobuf);
           66                 if(memcmp(v->byte, "\01CD001\01", 7) == 0){                /* iso */
           67                         if(dirp)
           68                                 putbuf(dirp);
           69                         dirp = p;
           70                         fmt = 'z';
           71                         dp = (Drec*)v->z.desc.rootdir;
           72                         blksize = l16(v->z.desc.blksize);
           73                         chat("iso, blksize=%d...", blksize);
           74 
           75                         v = (Voldesc*)(dirp->iobuf);
           76                         haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
           77                         if(haveplan9){
           78                                 if(noplan9) {
           79                                         chat("ignoring plan9");
           80                                         haveplan9 = 0;
           81                                 } else {
           82                                         fmt = '9';
           83                                         chat("plan9 iso...");
           84                                 }
           85                         }
           86                         continue;
           87                 }
           88 
           89                 if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){        /* high sierra */
           90                         if(dirp)
           91                                 putbuf(dirp);
           92                         dirp = p;
           93                         fmt = 'r';
           94                         dp = (Drec*)v->r.desc.rootdir;
           95                         blksize = l16(v->r.desc.blksize);
           96                         chat("high sierra, blksize=%d...", blksize);
           97                         continue;
           98                 }
           99 
          100                 if(haveplan9==0 && !nojoliet
          101                 && memcmp(v->byte, "\02CD001\01", 7) == 0){
          102 chat("%d %d\n", haveplan9, nojoliet);
          103                         /*
          104                          * The right thing to do is walk the escape sequences looking
          105                          * for one of 25 2F 4[035], but Microsoft seems to not honor
          106                          * the format, which makes it hard to walk over.
          107                          */
          108                         q = v->z.desc.escapes;
          109                         if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){        /* Joliet, it appears */
          110                                 if(dirp)
          111                                         putbuf(dirp);
          112                                 dirp = p;
          113                                 fmt = 'J';
          114                                 dp = (Drec*)v->z.desc.rootdir;
          115                                 if(blksize != l16(v->z.desc.blksize))
          116                                         fprint(2, "warning: suspicious Joliet blocksize\n");
          117                                 chat("joliet...");
          118                                 continue;
          119                         }
          120                 }
          121                 putbuf(p);
          122                 if(v->byte[0] == 0xFF)
          123                         break;
          124         }
          125 
          126         if(fmt == 0){
          127                 if(dirp)
          128                         putbuf(dirp);
          129                 return -1;
          130         }
          131         assert(dirp != nil);
          132 
          133         if(chatty)
          134                 showdrec(2, fmt, dp);
          135         if(blksize > Sectorsize){
          136                 chat("blksize too big...");
          137                 putbuf(dirp);
          138                 return -1;
          139         }
          140         if(waserror()){
          141                 putbuf(dirp);
          142                 nexterror();
          143         }
          144         root->len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen;
          145         root->ptr = fp = ealloc(root->len);
          146 
          147         if(haveplan9)
          148                 root->xf->isplan9 = 1;
          149 
          150         fp->fmt = fmt;
          151         fp->blksize = blksize;
          152         fp->offset = 0;
          153         fp->doffset = 0;
          154         memmove(&fp->d, dp, dp->z.reclen);
          155         root->qid.path = l32(dp->z.addr);
          156         root->qid.type = QTDIR;
          157         putbuf(dirp);
          158         poperror();
          159         if(getdrec(root, rd) >= 0){
          160                 n = rd->z.reclen-(34+rd->z.namelen);
          161                 s = (uchar*)rd->z.name + rd->z.namelen;
          162                 if((uintptr)s & 1){
          163                         s++;
          164                         n--;
          165                 }
          166                 if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
          167                    s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
          168                         root->xf->issusp = 1;
          169                         root->xf->suspoff = s[6];
          170                         n -= root->xf->suspoff;
          171                         s += root->xf->suspoff;
          172                         for(; n >= 4; s += l, n -= l){
          173                                 l = s[2];
          174                                 if(s[0] == 'E' && s[1] == 'R'){
          175                                         if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
          176                                                 root->xf->isrock = 1;
          177                                         break;
          178                                 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
          179                                         n = getcontin(root->xf->d, s, &s);
          180                                         continue;
          181                                 } else if(s[0] == 'R' && s[1] == 'R'){
          182                                         if(!norock)
          183                                                 root->xf->isrock = 1;
          184                                         break;
          185                                 } else if(s[0] == 'S' && s[1] == 'T')
          186                                         break;
          187                         }
          188                 }
          189         }
          190         if(root->xf->isrock)
          191                 chat("Rock Ridge...");
          192         fp->offset = 0;
          193         fp->doffset = 0;
          194         return 0;
          195 }
          196 
          197 static void
          198 iclone(Xfile *of, Xfile *nf)
          199 {
          200         USED(of);
          201         USED(nf);
          202 }
          203 
          204 static void
          205 iwalkup(Xfile *f)
          206 {
          207         long paddr;
          208         uchar dbuf[256];
          209         Drec *d = (Drec *)dbuf;
          210         Xfile pf, ppf;
          211         Isofile piso, ppiso;
          212 
          213         memset(&pf, 0, sizeof pf);
          214         memset(&ppf, 0, sizeof ppf);
          215         pf.ptr = &piso;
          216         ppf.ptr = &ppiso;
          217         if(opendotdot(f, &pf) < 0)
          218                 error("can't open pf");
          219         paddr = l32(pf.ptr->d.z.addr);
          220         if(l32(f->ptr->d.z.addr) == paddr)
          221                 return;
          222         if(opendotdot(&pf, &ppf) < 0)
          223                 error("can't open ppf");
          224         while(getdrec(&ppf, d) >= 0){
          225                 if(l32(d->z.addr) == paddr){
          226                         newdrec(f, d);
          227                         f->qid.path = paddr;
          228                         f->qid.type = QTDIR;
          229                         return;
          230                 }
          231         }
          232         error("can't find addr of ..");
          233 }
          234 
          235 static int
          236 casestrcmp(int isplan9, char *a, char *b)
          237 {
          238         int ca, cb;
          239 
          240         if(isplan9)
          241                 return strcmp(a, b);
          242         for(;;) {
          243                 ca = *a++;
          244                 cb = *b++;
          245                 if(ca >= 'A' && ca <= 'Z')
          246                         ca += 'a' - 'A';
          247                 if(cb >= 'A' && cb <= 'Z')
          248                         cb += 'a' - 'A';
          249                 if(ca != cb) {
          250                         if(ca > cb)
          251                                 return 1;
          252                         return -1;
          253                 }
          254                 if(ca == 0)
          255                         return 0;
          256         }
          257 }
          258 
          259 static void
          260 iwalk(Xfile *f, char *name)
          261 {
          262         Isofile *ip = f->ptr;
          263         uchar dbuf[256];
          264         char nbuf[4*Maxname];
          265         Drec *d = (Drec*)dbuf;
          266         Dir dir;
          267         char *p;
          268         int len, vers, dvers;
          269 
          270         vers = -1;
          271         if(p = strchr(name, ';')) {        /* assign = */
          272                 len = p-name;
          273                 if(len >= Maxname)
          274                         len = Maxname-1;
          275                 memmove(nbuf, name, len);
          276                 vers = strtoul(p+1, 0, 10);
          277                 name = nbuf;
          278         }
          279 /*
          280         len = strlen(name);
          281         if(len >= Maxname){
          282                 len = Maxname-1;
          283                 if(name != nbuf){
          284                         memmove(nbuf, name, len);
          285                         name = nbuf;
          286                 }
          287                 name[len] = 0;
          288         }
          289 */
          290 
          291         chat("%d \"%s\"...", strlen(name), name);
          292         ip->offset = 0;
          293         setnames(&dir, nbuf);
          294         while(getdrec(f, d) >= 0) {
          295                 dvers = rzdir(f->xf, &dir, ip->fmt, d);
          296                 if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
          297                         continue;
          298                 newdrec(f, d);
          299                 f->qid.path = dir.qid.path;
          300                 f->qid.type = dir.qid.type;
          301                 USED(dvers);
          302                 return;
          303         }
          304         USED(vers);
          305         error(Enonexist);
          306 }
          307 
          308 static void
          309 iopen(Xfile *f, int mode)
          310 {
          311         mode &= ~OCEXEC;
          312         if(mode != OREAD && mode != OEXEC)
          313                 error(Eperm);
          314         f->ptr->offset = 0;
          315         f->ptr->doffset = 0;
          316 }
          317 
          318 static void
          319 icreate(Xfile *f, char *name, long perm, int mode)
          320 {
          321         USED(f);
          322         USED(name);
          323         USED(perm);
          324         USED(mode);
          325         error(Eperm);
          326 }
          327 
          328 static long
          329 ireaddir(Xfile *f, uchar *buf, long offset, long count)
          330 {
          331         Isofile *ip = f->ptr;
          332         Dir d;
          333         char names[4*Maxname];
          334         uchar dbuf[256];
          335         Drec *drec = (Drec *)dbuf;
          336         int n, rcnt;
          337 
          338         if(offset==0){
          339                 ip->offset = 0;
          340                 ip->doffset = 0;
          341         }else if(offset != ip->doffset)
          342                 error("seek in directory not allowed");
          343 
          344         rcnt = 0;
          345         setnames(&d, names);
          346         while(rcnt < count && getdrec(f, drec) >= 0){
          347                 if(drec->z.namelen == 1){
          348                         if(drec->z.name[0] == 0)
          349                                 continue;
          350                         if(drec->z.name[0] == 1)
          351                                 continue;
          352                 }
          353                 rzdir(f->xf, &d, ip->fmt, drec);
          354                 d.qid.vers = f->qid.vers;
          355                 if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
          356                         ungetdrec(f);
          357                         break;
          358                 }
          359                 rcnt += n;
          360         }
          361         ip->doffset += rcnt;
          362         return rcnt;
          363 }
          364 
          365 static long
          366 iread(Xfile *f, char *buf, vlong offset, long count)
          367 {
          368         int n, o, rcnt = 0;
          369         long size;
          370         vlong addr;
          371         Isofile *ip = f->ptr;
          372         Iobuf *p;
          373 
          374         size = l32(ip->d.z.size);
          375         if(offset >= size)
          376                 return 0;
          377         if(offset+count > size)
          378                 count = size - offset;
          379         addr = ((vlong)l32(ip->d.z.addr) + ip->d.z.attrlen)*ip->blksize + offset;
          380         o = addr % Sectorsize;
          381         addr /= Sectorsize;
          382         /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.z.addr), addr, o);*/
          383         n = Sectorsize - o;
          384 
          385         while(count > 0){
          386                 if(n > count)
          387                         n = count;
          388                 p = getbuf(f->xf->d, addr);
          389                 memmove(&buf[rcnt], &p->iobuf[o], n);
          390                 putbuf(p);
          391                 count -= n;
          392                 rcnt += n;
          393                 ++addr;
          394                 o = 0;
          395                 n = Sectorsize;
          396         }
          397         return rcnt;
          398 }
          399 
          400 static long
          401 iwrite(Xfile *f, char *buf, vlong offset, long count)
          402 {
          403         USED(f);
          404         USED(buf);
          405         USED(offset);
          406         USED(count);
          407         error(Eperm);
          408         return 0;
          409 }
          410 
          411 static void
          412 iclunk(Xfile *f)
          413 {
          414         USED(f);
          415 }
          416 
          417 static void
          418 iremove(Xfile *f)
          419 {
          420         USED(f);
          421         error(Eperm);
          422 }
          423 
          424 static void
          425 istat(Xfile *f, Dir *d)
          426 {
          427         Isofile *ip = f->ptr;
          428 
          429         rzdir(f->xf, d, ip->fmt, &ip->d);
          430         d->qid.vers = f->qid.vers;
          431         if(d->qid.path==f->xf->rootqid.path){
          432                 d->qid.path = 0;
          433                 d->qid.type = QTDIR;
          434         }
          435 }
          436 
          437 static void
          438 iwstat(Xfile *f, Dir *d)
          439 {
          440         USED(f);
          441         USED(d);
          442         error(Eperm);
          443 }
          444 
          445 static int
          446 showdrec(int fd, int fmt, void *x)
          447 {
          448         Drec *d = (Drec *)x;
          449         int namelen;
          450         int syslen;
          451 
          452         if(d->z.reclen == 0)
          453                 return 0;
          454         fprint(fd, "%d %d %ld %ld ",
          455                 d->z.reclen, d->z.attrlen, l32(d->z.addr), l32(d->z.size));
          456         fprint(fd, "%s 0x%2.2x %d %d %ld ",
          457                 rdate(d->z.date, fmt), (fmt=='z' ? d->z.flags : d->r.flags),
          458                 d->z.unitsize, d->z.gapsize, l16(d->z.vseqno));
          459         fprint(fd, "%d %s", d->z.namelen, nstr(d->z.name, d->z.namelen));
          460         if(fmt != 'J'){
          461                 namelen = d->z.namelen + (1-(d->z.namelen&1));
          462                 syslen = d->z.reclen - 33 - namelen;
          463                 if(syslen != 0)
          464                         fprint(fd, " %s", nstr(&d->z.name[namelen], syslen));
          465         }
          466         fprint(fd, "\n");
          467         return d->z.reclen + (d->z.reclen&1);
          468 }
          469 
          470 static void
          471 newdrec(Xfile *f, Drec *dp)
          472 {
          473         Isofile *x = f->ptr;
          474         Isofile *n;
          475         int len;
          476 
          477         len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen;
          478         n = ealloc(len);
          479         n->fmt = x->fmt;
          480         n->blksize = x->blksize;
          481         n->offset = 0;
          482         n->doffset = 0;
          483         memmove(&n->d, dp, dp->z.reclen);
          484         free(x);
          485         f->ptr = n;
          486         f->len = len;
          487 }
          488 
          489 static void
          490 ungetdrec(Xfile *f)
          491 {
          492         Isofile *ip = f->ptr;
          493 
          494         if(ip->offset >= ip->odelta){
          495                 ip->offset -= ip->odelta;
          496                 ip->odelta = 0;
          497         }
          498 }
          499 
          500 static int
          501 getdrec(Xfile *f, void *buf)
          502 {
          503         Isofile *ip = f->ptr;
          504         int len = 0, boff = 0;
          505         ulong size;
          506         vlong addr;
          507         Iobuf *p = 0;
          508 
          509         if(!ip)
          510                 return -1;
          511         size = l32(ip->d.z.size);
          512         while(ip->offset < size){
          513                 addr = (l32(ip->d.z.addr)+ip->d.z.attrlen)*ip->blksize + ip->offset;
          514                 boff = addr % Sectorsize;
          515                 if(boff > Sectorsize-34){
          516                         ip->offset += Sectorsize-boff;
          517                         continue;
          518                 }
          519                 p = getbuf(f->xf->d, addr/Sectorsize);
          520                 len = p->iobuf[boff];
          521                 if(len >= 34)
          522                         break;
          523                 putbuf(p);
          524                 p = 0;
          525                 ip->offset += Sectorsize-boff;
          526         }
          527         if(p) {
          528                 memmove(buf, &p->iobuf[boff], len);
          529                 putbuf(p);
          530                 ip->odelta = len + (len&1);
          531                 ip->offset += ip->odelta;
          532                 return 0;
          533         }
          534         return -1;
          535 }
          536 
          537 static int
          538 opendotdot(Xfile *f, Xfile *pf)
          539 {
          540         uchar dbuf[256];
          541         Drec *d = (Drec *)dbuf;
          542         Isofile *ip = f->ptr, *pip = pf->ptr;
          543 
          544         ip->offset = 0;
          545         if(getdrec(f, d) < 0){
          546                 chat("opendotdot: getdrec(.) failed...");
          547                 return -1;
          548         }
          549         if(d->z.namelen != 1 || d->z.name[0] != 0){
          550                 chat("opendotdot: no . entry...");
          551                 return -1;
          552         }
          553         if(l32(d->z.addr) != l32(ip->d.z.addr)){
          554                 chat("opendotdot: bad . address...");
          555                 return -1;
          556         }
          557         if(getdrec(f, d) < 0){
          558                 chat("opendotdot: getdrec(..) failed...");
          559                 return -1;
          560         }
          561         if(d->z.namelen != 1 || d->z.name[0] != 1){
          562                 chat("opendotdot: no .. entry...");
          563                 return -1;
          564         }
          565 
          566         pf->xf = f->xf;
          567         pip->fmt = ip->fmt;
          568         pip->blksize = ip->blksize;
          569         pip->offset = 0;
          570         pip->doffset = 0;
          571         pip->d = *d;
          572         return 0;
          573 }
          574 
          575 enum {
          576         Hname = 1,
          577         Hmode = 2,
          578 };
          579 
          580 static int
          581 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
          582 {
          583         int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
          584         uchar *s;
          585         char *p;
          586         char buf[Maxname+UTFmax+1];
          587         uchar *q;
          588         Rune r;
          589         enum { ONAMELEN = 28 };        /* old Plan 9 directory name length */
          590 
          591         have = 0;
          592         flags = 0;
          593         vers = -1;
          594         d->qid.path = l32(dp->z.addr);
          595         d->qid.type = 0;
          596         d->qid.vers = 0;
          597         n = dp->z.namelen;
          598         memset(d->name, 0, Maxname);
          599         if(n == 1) {
          600                 switch(dp->z.name[0]){
          601                 case 1:
          602                         d->name[1] = '.';
          603                         /* fall through */
          604                 case 0:
          605                         d->name[0] = '.';
          606                         have = Hname;
          607                         break;
          608                 default:
          609                         d->name[0] = tolower(dp->z.name[0]);
          610                 }
          611         } else {
          612                 if(fmt == 'J'){        /* Joliet, 16-bit Unicode */
          613                         q = (uchar*)dp->z.name;
          614                         for(i=j=lj=0; i<n && j<Maxname; i+=2){
          615                                 lj = j;
          616                                 r = (q[i]<<8)|q[i+1];
          617                                 j += runetochar(buf+j, &r);
          618                         }
          619                         if(j >= Maxname)
          620                                 j = lj;
          621                         memmove(d->name, buf, j);
          622                 }else{
          623                         if(n >= Maxname)
          624                                 n = Maxname-1;
          625                         for(i=0; i<n; i++)
          626                                 d->name[i] = tolower(dp->z.name[i]);
          627                 }
          628         }
          629 
          630         sysl = dp->z.reclen-(34+dp->z.namelen);
          631         s = (uchar*)dp->z.name + dp->z.namelen;
          632         if(((uintptr)s) & 1) {
          633                 s++;
          634                 sysl--;
          635         }
          636         if(fs->isplan9 && sysl > 0) {
          637                 /*
          638                  * get gid, uid, mode and possibly name
          639                  * from plan9 directory extension
          640                  */
          641                 nl = *s;
          642                 if(nl >= ONAMELEN)
          643                         nl = ONAMELEN-1;
          644                 if(nl) {
          645                         memset(d->name, 0, ONAMELEN);
          646                         memmove(d->name, s+1, nl);
          647                 }
          648                 s += 1 + *s;
          649                 nl = *s;
          650                 if(nl >= ONAMELEN)
          651                         nl = ONAMELEN-1;
          652                 memset(d->uid, 0, ONAMELEN);
          653                 memmove(d->uid, s+1, nl);
          654                 s += 1 + *s;
          655                 nl = *s;
          656                 if(nl >= ONAMELEN)
          657                         nl = ONAMELEN-1;
          658                 memset(d->gid, 0, ONAMELEN);
          659                 memmove(d->gid, s+1, nl);
          660                 s += 1 + *s;
          661                 if(((uintptr)s) & 1)
          662                         s++;
          663                 d->mode = l32(s);
          664                 if(d->mode & DMDIR)
          665                         d->qid.type |= QTDIR;
          666         } else {
          667                 d->mode = 0444;
          668                 switch(fmt) {
          669                 case 'z':
          670                         if(fs->isrock)
          671                                 strcpy(d->gid, "ridge");
          672                         else
          673                                 strcpy(d->gid, "iso9660");
          674                         flags = dp->z.flags;
          675                         break;
          676                 case 'r':
          677                         strcpy(d->gid, "sierra");
          678                         flags = dp->r.flags;
          679                         break;
          680                 case 'J':
          681                         strcpy(d->gid, "joliet");
          682                         flags = dp->z.flags;
          683                         break;
          684                 case '9':
          685                         strcpy(d->gid, "plan9");
          686                         flags = dp->z.flags;
          687                         break;
          688                 }
          689                 if(flags & 0x02){
          690                         d->qid.type |= QTDIR;
          691                         d->mode |= DMDIR|0111;
          692                 }
          693                 strcpy(d->uid, "cdrom");
          694                 if(fmt!='9' && !(d->mode&DMDIR)){
          695                         /*
          696                          * ISO 9660 actually requires that you always have a . and a ;,
          697                          * even if there is no version and no extension.  Very few writers
          698                          * do this.  If the version is present, we use it for qid.vers.
          699                          * If there is no extension but there is a dot, we strip it off.
          700                          * (VMS heads couldn't comprehend the dot as a file name character
          701                          * rather than as just a separator between name and extension.)
          702                          *
          703                          * We don't do this for directory names because directories are
          704                          * not allowed to have extensions and versions.
          705                          */
          706                         if((p=strchr(d->name, ';')) != nil){
          707                                 vers = strtoul(p+1, 0, 0);
          708                                 d->qid.vers = vers;
          709                                 *p = '\0';
          710                         }
          711                         if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
          712                                 *p = '\0';
          713                 }
          714                 if(fs->issusp){
          715                         nl = 0;
          716                         s += fs->suspoff;
          717                         sysl -= fs->suspoff;
          718                         for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
          719                                 if(s[0] == 0 && ((uintptr)s & 1)){
          720                                         /* MacOS pads individual entries, contrary to spec */
          721                                         s++;
          722                                         sysl--;
          723                                 }
          724                                 l = s[2];
          725                                 if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
          726                                         /* posix file attributes */
          727                                         mode = l32(s+4);
          728                                         d->mode = mode & 0777;
          729                                         if((mode & 0170000) == 040000){
          730                                                 d->mode |= DMDIR;
          731                                                 d->qid.type |= QTDIR;
          732                                         }
          733                                         have |= Hmode;
          734                                 } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
          735                                         /* alternative name */
          736                                         if((s[4] & ~1) == 0){
          737                                                 i = nl+l-5;
          738                                                 if(i >= Maxname)
          739                                                         i = Maxname-1;
          740                                                 if((i -= nl) > 0){
          741                                                         memmove(d->name+nl, s+5, i);
          742                                                         nl += i;
          743                                                 }
          744                                                 if(s[4] == 0)
          745                                                         have |= Hname;
          746                                         }
          747                                 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
          748                                         sysl = getcontin(fs->d, s, &s);
          749                                         continue;
          750                                 } else if(s[0] == 'S' && s[1] == 'T')
          751                                         break;
          752                         }
          753                 }
          754         }
          755         d->length = 0;
          756         if((d->mode & DMDIR) == 0)
          757                 d->length = l32(dp->z.size);
          758         d->type = 0;
          759         d->dev = 0;
          760         d->atime = gtime(dp->z.date);
          761         d->mtime = d->atime;
          762         return vers;
          763 }
          764 
          765 static int
          766 getcontin(Xdata *dev, uchar *p, uchar **s)
          767 {
          768         long bn, off, len;
          769         Iobuf *b;
          770 
          771         bn = l32(p+4);
          772         off = l32(p+12);
          773         len = l32(p+20);
          774         chat("getcontin %d...", bn);
          775         b = getbuf(dev, bn);
          776         if(b == 0){
          777                 *s = 0;
          778                 return 0;
          779         }
          780         *s = b->iobuf+off;
          781         putbuf(b);
          782         return len;
          783 }
          784 
          785 static char *
          786 nstr(uchar *p, int n)
          787 {
          788         static char buf[132];
          789         char *q = buf;
          790 
          791         while(--n >= 0){
          792                 if(*p == '\\')
          793                         *q++ = '\\';
          794                 if(' ' <= *p && *p <= '~')
          795                         *q++ = *p++;
          796                 else
          797                         q += sprint(q, "\\%2.2ux", *p++);
          798         }
          799         *q = 0;
          800         return buf;
          801 }
          802 
          803 static char *
          804 rdate(uchar *p, int fmt)
          805 {
          806         static char buf[64];
          807         int htz, s, n;
          808 
          809         n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
          810                 p[0], p[1], p[2], p[3], p[4], p[5]);
          811         if(fmt == 'z'){
          812                 htz = p[6];
          813                 if(htz >= 128){
          814                         htz = 256-htz;
          815                         s = '-';
          816                 }else
          817                         s = '+';
          818                 sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
          819         }
          820         return buf;
          821 }
          822 
          823 static char
          824 dmsize[12] =
          825 {
          826         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
          827 };
          828 
          829 #define dysize mydysize
          830 
          831 static int
          832 dysize(int y)
          833 {
          834 
          835         if((y%4) == 0)
          836                 return 366;
          837         return 365;
          838 }
          839 
          840 static long
          841 gtime(uchar *p)        /* yMdhmsz */
          842 {
          843         long t;
          844         int i, y, M, d, h, m, s, tz;
          845 
          846         y=p[0]; M=p[1]; d=p[2];
          847         h=p[3]; m=p[4]; s=p[5]; tz=p[6];
          848         USED(tz);
          849         y += 1900;
          850         if (y < 1970)
          851                 return 0;
          852         if (M < 1 || M > 12)
          853                 return 0;
          854         if (d < 1 || d > dmsize[M-1])
          855                 if (!(M == 2 && d == 29 && dysize(y) == 366))
          856                         return 0;
          857         if (h > 23)
          858                 return 0;
          859         if (m > 59)
          860                 return 0;
          861         if (s > 59)
          862                 return 0;
          863         t = 0;
          864         for(i=1970; i<y; i++)
          865                 t += dysize(i);
          866         if (dysize(y)==366 && M >= 3)
          867                 t++;
          868         while(--M)
          869                 t += dmsize[M-1];
          870         t += d-1;
          871         t = 24*t + h;
          872         t = 60*t + m;
          873         t = 60*t + s;
          874         return t;
          875 }
          876 
          877 #define        p        ((uchar*)arg)
          878 
          879 static long
          880 l16(void *arg)
          881 {
          882         long v;
          883 
          884         v = ((long)p[1]<<8)|p[0];
          885         if (v >= 0x8000L)
          886                 v -= 0x10000L;
          887         return v;
          888 }
          889 
          890 static long
          891 l32(void *arg)
          892 {
          893         return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
          894 }
          895 
          896 #undef        p