ref: cbe4b116989c0bfec49b9ae7a1d9a85ead6648ae
dir: /sys/src/cmd/dossrv/dossubs.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <auth.h> #include <fcall.h> #include "iotrack.h" #include "dat.h" #include "fns.h" static uchar isdos[256]; int isdosfs(uchar *buf) { /* * When dynamic disc managers move the disc partition, * they make it start with 0xE9. */ if(buf[0] == 0xE9) return 1; /* * Check if the jump displacement (magic[1]) is too short for a FAT. * * check now omitted due to digital cameras that use a 0 jump. * the ecma-107 standard says this is okay and that interoperable fat * implementations shouldn't assume this: * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf, * page 11. */ if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */) return 1; if(chatty) fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]); return 0; } int dosfs(Xfs *xf) { Iosect *p1; Fatinfo *fi; uchar buf[512]; Dosboot *b; Dosboot32 *b32; Dosbpb *bp; long fisec, extflags; int i; if(!isdos['a']){ for(i = 'a'; i <= 'z'; i++) isdos[i] = 1; for(i = 'A'; i <= 'Z'; i++) isdos[i] = 1; for(i = '0'; i <= '9'; i++) isdos[i] = 1; isdos['$'] = 1; isdos['%'] = 1; isdos['''] = 1; isdos['-'] = 1; isdos['_'] = 1; isdos['@'] = 1; isdos['~'] = 1; isdos['`'] = 1; isdos['!'] = 1; isdos['('] = 1; isdos[')'] = 1; isdos['{'] = 1; isdos['}'] = 1; isdos['^'] = 1; isdos['#'] = 1; isdos['&'] = 1; } /* don't use getsect() before determining sector size */ if(devread(xf, 0, buf, sizeof(buf)) < 0) return -1; if(!isdosfs(buf)) return -1; bp = malloc(sizeof(Dosbpb)); if(bp == nil) return -1; memset(bp, 0, sizeof(Dosbpb)); /* clear lock */ b = (Dosboot*)buf; xf->sectsize = GSHORT(b->sectsize); if(xf->sectsize < 32){ if(chatty) fprint(2, "sectsize %d\n", xf->sectsize); goto badfmt; } xf->sect2trk = GSHORT(b->trksize); if(xf->sect2trk == 0){ if(chatty) fprint(2, "sect2trk %d\n", xf->sect2trk); goto badfmt; } bp->clustsize = b->clustsize; bp->nresrv = GSHORT(b->nresrv); bp->nfats = b->nfats; bp->rootsize = GSHORT(b->rootsize); bp->volsize = GSHORT(b->volsize); if(bp->volsize == 0) bp->volsize = GLONG(b->bigvolsize); bp->mediadesc = b->mediadesc; bp->fatsize = GSHORT(b->fatsize); bp->fataddr = GSHORT(b->nresrv); bp->fatinfo = 0; if(bp->fatsize == 0){ /* is FAT32 */ b32 = (Dosboot32*)buf; if(chatty) bootsecdump32(2, xf, b32); xf->isfat32 = 1; bp->fatsize = GLONG(b32->fatsize32); if(bp->fatsize == 0){ if(chatty) fprint(2, "fatsize 0\n"); goto badfmt; } bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize; bp->rootaddr = 0; bp->rootstart = GLONG(b32->rootstart); /* * disable fat mirroring? */ extflags = GSHORT(b32->extflags); if(extflags & 0x0080){ for(i = 0; i < 4; i++){ if(extflags & (1 << i)){ bp->fataddr += i * bp->fatsize; bp->nfats = 1; break; } } } /* * fat free list info */ bp->freeptr = FATRESRV; fisec = GSHORT(b32->infospec); if(fisec != 0 && fisec < GSHORT(b32->nresrv)){ p1 = getsect(xf, fisec); if(p1 != nil){ fi = (Fatinfo*)p1->iobuf; if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){ bp->fatinfo = fisec; bp->freeptr = GLONG(fi->nextfree); bp->freeclusters = GLONG(fi->freeclust); chat("fat info: %ld free clusters, next free %ld\n", bp->freeclusters, bp->freeptr); } putsect(p1); } } }else{ if(chatty) bootdump(2, b); bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize; bp->rootstart = 0; bp->dataaddr = bp->rootaddr + (bp->rootsize*DOSDIRSIZE + xf->sectsize-1)/xf->sectsize; bp->freeptr = FATRESRV; } if(bp->clustsize == 0){ if(chatty) fprint(2, "clustsize 0\n"); goto badfmt; } if(bp->volsize < bp->dataaddr){ if(chatty) fprint(2, "volsize < dataaddr\n"); goto badfmt; } bp->fatclusters = FATRESRV + (bp->volsize - bp->dataaddr)/bp->clustsize; if(xf->isfat32) bp->fatbits = 32; else if(bp->fatclusters < 4087) bp->fatbits = 12; else bp->fatbits = 16; chat("fatbits=%d (%ld clusters)...", bp->fatbits, bp->fatclusters); for(i=0; i<b->nfats; i++) chat("fat %d: %lld...", i, bp->fataddr+i*bp->fatsize); chat("root: %lld...", bp->rootaddr); chat("data: %lld...", bp->dataaddr); xf->ptr = bp; return 0; badfmt: free(bp); return -1; } /* * initialize f to the root directory * this file has no Dosdir entry, * so we special case it all over. */ void rootfile(Xfile *f) { Dosptr *dp; dp = f->ptr; memset(dp, 0, sizeof(Dosptr)); dp->prevaddr = -1; } int isroot(vlong addr) { return addr == 0; } int getfile(Xfile *f) { Dosptr *dp; Iosect *p; dp = f->ptr; if(dp->p != nil) panic("getfile"); p = getsect(f->xf, dp->addr); if(p == nil) return -1; /* * we could also make up a Dosdir for the root */ dp->d = nil; if(!isroot(dp->addr)){ if(f->qid.path != QIDPATH(dp, f->xf)){ chat("qid mismatch f=%#llux d=%#llux...", f->qid.path, QIDPATH(dp, f->xf)); putsect(p); errno = Enonexist; return -1; } dp->d = (Dosdir *)&p->iobuf[dp->offset]; } dp->p = p; return 0; } void putfile(Xfile *f) { Dosptr *dp; dp = f->ptr; if(dp->p == nil) panic("putfile"); putsect(dp->p); dp->p = nil; dp->d = nil; } long getstart(Xfs *xf, Dosdir *d) { long start; start = GSHORT(d->start); if(xf->isfat32) start |= GSHORT(d->hstart)<<16; return start; } void putstart(Xfs *xf, Dosdir *d, long start) { PSHORT(d->start, start); if(xf->isfat32) PSHORT(d->hstart, start>>16); } /* * return the disk cluster for the iclust cluster in f */ long fileclust(Xfile *f, long iclust, int cflag) { Xfs *xf = f->xf; Dosbpb *bp = xf->ptr; Dosptr *dp = f->ptr; Dosdir *d = dp->d; long start, clust, nskip, next = 0; /* * asking for the cluster of the root directory * is not a well-formed question, since the root directory * does not begin on a cluster boundary. */ if(!xf->isfat32 && isroot(dp->addr)) return -1; if(xf->isfat32 && isroot(dp->addr)){ start = bp->rootstart; }else{ start = getstart(xf, d); if(start == 0){ if(!cflag) return -1; mlock(bp); start = falloc(xf); unmlock(bp); if(start <= 0) return -1; puttime(d, 0); putstart(f->xf, d, start); dp->p->flags |= BMOD; dp->clust = 0; } } if(dp->clust == 0 || iclust < dp->iclust){ clust = start; nskip = iclust; }else{ clust = dp->clust; nskip = iclust - dp->iclust; } if(chatty > 1 && nskip > 0) chat("clust %#lx, skip %ld...", clust, nskip); if(clust <= 0) return -1; if(nskip > 0){ mlock(bp); while(--nskip >= 0){ next = getfat(xf, clust); if(chatty > 1) chat("->%#lx", next); if(next > 0){ clust = next; continue; }else if(!cflag) break; if(d && (d->attr&DSYSTEM)){ next = cfalloc(f); if(next < 0) break; /* cfalloc will call putfat for us, since clust may change */ } else { next = falloc(xf); if(next < 0) break; putfat(xf, clust, next); } clust = next; } unmlock(bp); if(next <= 0) return -1; dp->clust = clust; dp->iclust = iclust; } if(chatty > 1) chat(" clust(%#lx)=%#lx...", iclust, clust); return clust; } /* * return the disk sector for the isect disk sector in f */ vlong fileaddr(Xfile *f, long isect, int cflag) { Xfs *xf = f->xf; Dosbpb *bp; Dosptr *dp; long clust; bp = xf->ptr; dp = f->ptr; if(!f->xf->isfat32 && isroot(dp->addr)){ if(isect*xf->sectsize >= bp->rootsize*DOSDIRSIZE) return -1; return bp->rootaddr + isect; } clust = fileclust(f, isect/bp->clustsize, cflag); if(clust < 0) return -1; return clust2sect(bp, clust) + isect%bp->clustsize; } /* * translate names */ void fixname(char *buf) { int c; char *p; p = buf; while(c = *p){ if(c == ':' && trspaces) *p = ' '; p++; } } /* * classify the file name as one of * Invalid - contains a bad character * Short - short valid 8.3 name, no lowercase letters * ShortLower - short valid 8.3 name except for lowercase letters * Long - long name */ int classifyname(char *buf) { char *p, *dot; int c, isextended, is8dot3, islower, ndot; p = buf; isextended = 0; islower = 0; dot = nil; ndot = 0; while(c = (uchar)*p){ if(c&0x80) /* UTF8 */ isextended = 1; else if(c == '.'){ dot = p; ndot++; }else if(strchr("+,:;=[] ", c)) isextended = 1; else if(!isdos[c]) return Invalid; if('a' <= c && c <= 'z') islower = 1; p++; } is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3); if(!isextended && is8dot3){ if(islower) return ShortLower; return Short; } return Long; } /* * make an alias for a valid long file name */ void mkalias(char *name, char *sname, int id) { Rune r; char *s, *e, sid[10]; int i, esuf, v; e = strrchr(name, '.'); if(e == nil) e = strchr(name, '\0'); s = name; i = 0; while(s < e && i < 6){ if(isdos[(uchar)*s]) sname[i++] = *s++; else s += chartorune(&r, s); } v = snprint(sid, 10, "%d", id); if(i + 1 + v > 8) i = 8 - 1 - v; sname[i++] = '~'; strcpy(&sname[i], sid); i += v; sname[i++] = '.'; esuf = i + 3; if(esuf > 12) panic("bad mkalias"); while(*e && i < esuf){ if(isdos[(uchar)*e]) sname[i++] = *e++; else e += chartorune(&r, e); } if(sname[i-1] == '.') i--; sname[i] = '\0'; } /* * check for valid plan 9 names, * rewrite ' ' to ':' */ char isfrog[256]={ /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, /* [' '] 1, let's try this -rsc */ ['/'] 1, [0x7f] 1, }; int nameok(char *elem) { while(*elem) { if(*elem == ' ' && trspaces) *elem = ':'; if(isfrog[*(uchar*)elem]) return 0; elem++; } return 1; } /* * look for a directory entry matching name * always searches for long names which match a short name */ int searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype) { Xfs *xf; Iosect *p; Dosdir *d; char buf[261], *bname; int isect, o, o1, islong, have, need, sum; vlong addr, addr1, addr2, prevaddr, prevaddr1; xf = f->xf; addr1 = -1; addr2 = -1; prevaddr1 = -1; o1 = 0; islong = 0; sum = -1; need = 1; if(longtype!=Short && cflag) need += (utflen(name) + DOSRUNE-1) / DOSRUNE; memset(dp, 0, sizeof(Dosptr)); dp->prevaddr = -1; dp->naddr = -1; dp->paddr = ((Dosptr *)f->ptr)->addr; dp->poffset = ((Dosptr *)f->ptr)->offset; have = 0; addr = -1; bname = nil; for(isect=0;; isect++){ prevaddr = addr; addr = fileaddr(f, isect, cflag); if(addr < 0) break; p = getsect(xf, addr); if(p == nil) break; for(o=0; o<xf->sectsize; o+=DOSDIRSIZE){ d = (Dosdir *)&p->iobuf[o]; if(d->name[0] == 0x00){ chat("end dir(0)..."); putsect(p); if(!cflag) return -1; /* * addr1 & o1 are the start of the dirs * addr2 is the optional second cluster used if the long name * entry does not fit within the addr1 cluster * * have tells us the number of contiguous free dirs * starting at addr1.o1; need are necessary to hold the long name. */ if(addr1 < 0){ addr1 = addr; prevaddr1 = prevaddr; o1 = o; } if(addr2 < 0 && (xf->sectsize-o)/DOSDIRSIZE + have < need){ addr2 = fileaddr(f, isect+1, cflag); if(addr2 < 0) goto breakout; }else if(addr2 < 0) addr2 = addr; if(addr2 == addr1) addr2 = -1; dp->addr = addr1; dp->offset = o1; dp->prevaddr = prevaddr1; dp->naddr = addr2; return 0; } if(d->name[0] == DOSEMPTY){ if(chatty) fprint(2, "empty dir\n"); have++; if(addr1 == -1){ addr1 = addr; o1 = o; prevaddr1 = prevaddr; } if(addr2 == -1 && have >= need) addr2 = addr; continue; } have = 0; if(addr2 == -1) addr1 = -1; dirdump(d); if((d->attr & 0xf) == 0xf){ bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1); continue; } if(d->attr & DVLABEL){ islong = 0; continue; } if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){ bname = buf; getname(buf, d); } islong = 0; if(cistrcmp(bname, name) != 0) continue; if(chatty) fprint(2, "found\n"); if(cflag){ putsect(p); return -1; } dp->addr = addr; dp->prevaddr = prevaddr; dp->offset = o; dp->p = p; dp->d = d; return 0; } putsect(p); } breakout: chat("end dir(1)..."); return -1; } int emptydir(Xfile *f) { Xfs *xf = f->xf; int isect, o; vlong addr; Iosect *p; Dosdir *d; for(isect=0;; isect++){ addr = fileaddr(f, isect, 0); if(addr < 0) break; p = getsect(xf, addr); if(p == nil) return -1; for(o=0; o<xf->sectsize; o+=DOSDIRSIZE){ d = (Dosdir *)&p->iobuf[o]; if(d->name[0] == 0x00){ putsect(p); return 0; } if(d->name[0] == DOSEMPTY) continue; if(d->name[0] == '.') continue; if(d->attr&DVLABEL) continue; putsect(p); return -1; } putsect(p); } return 0; } long readdir(Xfile *f, void *vbuf, vlong offset, long count) { Xfs *xf; Dir dir; int isect, o, islong, sum; vlong addr; Iosect *p; Dosdir *d; long rcnt, n; char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN]; uchar *buf; buf = vbuf; rcnt = 0; xf = f->xf; if(count <= 0) return 0; islong = 0; sum = -1; name = nil; for(isect=0;; isect++){ addr = fileaddr(f, isect, 0); if(addr < 0) break; p = getsect(xf, addr); if(p == nil) return -1; for(o=0; o<xf->sectsize; o+=DOSDIRSIZE){ d = (Dosdir *)&p->iobuf[o]; if(d->name[0] == 0x00){ putsect(p); return rcnt; } if(d->name[0] == DOSEMPTY) continue; dirdump(d); if(d->name[0] == '.'){ if(d->name[1] == ' ' || d->name[1] == 0) continue; if(d->name[1] == '.' && (d->name[2] == ' ' || d->name[2] == 0)) continue; } if((d->attr & 0xf) == 0xf){ name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1); continue; } if(d->attr & DVLABEL){ islong = 0; continue; } dir.name = snamebuf; getdir(xf, &dir, d, addr, o); if(islong == 1 && nameok(name) && sum == aliassum(d)) dir.name = name; islong = 0; n = convD2M(&dir, &buf[rcnt], count - rcnt); name = nil; if(n <= BIT16SZ){ /* no room for next entry */ putsect(p); return rcnt; } rcnt += n; if(offset > 0){ offset -= rcnt; rcnt = 0; islong = 0; continue; } if(rcnt == count){ putsect(p); return rcnt; } } putsect(p); } return rcnt; } /* * set up ndp for a directory's parent * the hardest part is setting up paddr */ int walkup(Xfile *f, Dosptr *ndp) { Xfs *xf = f->xf; Dosbpb *bp = xf->ptr; Dosptr *dp = f->ptr; Dosdir *xd; Iosect *p; long o, so, start, pstart, ppstart, st, ppclust; vlong k; memset(ndp, 0, sizeof(Dosptr)); ndp->prevaddr = -1; ndp->naddr = -1; ndp->addr = dp->paddr; ndp->offset = dp->poffset; chat("walkup: paddr=%#llx...", dp->paddr); /* * root's paddr is always itself */ if(isroot(dp->paddr)) return 0; /* * find the start of our parent's directory */ p = getsect(xf, dp->paddr); if(p == nil) goto error; xd = (Dosdir *)&p->iobuf[dp->poffset]; dirdump(xd); start = getstart(xf, xd); chat("start=%#lx...", start); if(start == 0) goto error; putsect(p); /* * verify that parent's . points to itself */ p = getsect(f->xf, clust2sect(bp, start)); if(p == nil) goto error; xd = (Dosdir *)p->iobuf; dirdump(xd); st = getstart(xf, xd); if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st) goto error; /* * parent's .. is the next entry, and has start of parent's parent */ xd++; dirdump(xd); if(xd->name[0] != '.' || xd->name[1] != '.') goto error; pstart = getstart(xf, xd); putsect(p); /* * we're done if parent is root */ if(pstart == 0 || xf->isfat32 && pstart == bp->rootstart) return 0; /* * verify that parent's . points to itself */ p = getsect(xf, clust2sect(bp, pstart)); if(p == nil){ chat("getsect %ld failed\n", pstart); goto error; } xd = (Dosdir *)p->iobuf; dirdump(xd); st = getstart(xf, xd); if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st) goto error; /* * parent's parent's .. is the next entry, and has start of parent's parent's parent */ xd++; dirdump(xd); if(xd->name[0] != '.' || xd->name[1] != '.') goto error; ppstart = getstart(xf, xd); putsect(p); /* * open parent's parent's parent, and walk through it until parent's parent is found * need this to find parent's parent's addr and offset */ ppclust = ppstart; if(xf->isfat32 && ppclust == 0){ ppclust = bp->rootstart; chat("ppclust 0, resetting to rootstart\n"); } k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr; p = getsect(xf, k); if(p == nil){ chat("getsect %lld failed\n", k); goto error; } xd = (Dosdir *)p->iobuf; dirdump(xd); if(ppstart){ st = getstart(xf, xd); if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st) goto error; } for(so=1;; so++){ for(o=0; o<xf->sectsize; o+=DOSDIRSIZE){ xd = (Dosdir *)&p->iobuf[o]; if(xd->name[0] == 0x00){ chat("end dir\n"); goto error; } if(xd->name[0] == DOSEMPTY) continue; st = getstart(xf, xd); if(st == pstart) goto out; } if(ppclust){ if(so%bp->clustsize == 0){ mlock(bp); ppclust = getfat(xf, ppclust); unmlock(bp); if(ppclust < 0){ chat("getfat %ld failed\n", ppclust); goto error; } } k = clust2sect(bp, ppclust) + so%bp->clustsize; }else{ if(so*xf->sectsize >= bp->rootsize*DOSDIRSIZE) goto error; k = bp->rootaddr + so; } putsect(p); p = getsect(xf, k); if(p == nil){ chat("getsect %lld failed\n", k); goto error; } } out: putsect(p); ndp->paddr = k; ndp->poffset = o; return 0; error: if(p != nil) putsect(p); return -1; } long readfile(Xfile *f, void *vbuf, vlong offset, long count) { Xfs *xf = f->xf; Dosptr *dp = f->ptr; Dosdir *d = dp->d; int isect, o, c; vlong addr; Iosect *p; uchar *buf; ulong length, rcnt; if(offset >= MAXFILELEN) return 0; if(offset+count > MAXFILELEN) count = MAXFILELEN - offset; rcnt = 0; length = GLONG(d->length); buf = vbuf; if(offset >= length) return 0; if(offset+count >= length) count = length - offset; isect = offset/xf->sectsize; o = offset%xf->sectsize; while(count > 0){ addr = fileaddr(f, isect++, 0); if(addr < 0) break; c = xf->sectsize - o; if(c > count) c = count; p = getsect(xf, addr); if(p == nil) return -1; memmove(&buf[rcnt], &p->iobuf[o], c); putsect(p); count -= c; rcnt += c; o = 0; } return rcnt; } long writefile(Xfile *f, void *vbuf, vlong offset, long count) { Xfs *xf = f->xf; Dosbpb *bp = xf->ptr; Dosptr *dp = f->ptr; Dosdir *d = dp->d; int isect, o, c; vlong addr; Iosect *p; uchar *buf; ulong length, rcnt, dlen; if(offset >= MAXFILELEN) return 0; if(offset+count > MAXFILELEN) count = MAXFILELEN - offset; rcnt = 0; addr = 0; buf = vbuf; isect = offset/xf->sectsize; o = offset%xf->sectsize; while(count > 0){ addr = fileaddr(f, isect++, 1); if(addr < 0) break; c = xf->sectsize - o; if(c > count) c = count; if(c == xf->sectsize){ p = getosect(xf, addr); p->flags = 0; }else{ p = getsect(xf, addr); if(p == nil) return -1; } memmove(&p->iobuf[o], &buf[rcnt], c); p->flags |= BMOD; putsect(p); count -= c; rcnt += c; o = 0; } if(addr < 0) return -1; length = 0; dlen = GLONG(d->length); if(rcnt > 0) length = offset+rcnt; else if(dp->addr && dp->clust){ c = bp->clustsize*xf->sectsize; if(dp->iclust > (dlen+c-1)/c) length = (ulong)c*dp->iclust; } if(length > dlen) PLONG(d->length, length); puttime(d, 0); dp->p->flags |= BMOD; return rcnt; } int truncfile(Xfile *f, vlong length) { Xfs *xf = f->xf; Dosbpb *bp = xf->ptr; Dosptr *dp = f->ptr; Dosdir *d = dp->d; long clust, next; vlong n; if(length > MAXFILELEN) return -1; mlock(bp); clust = getstart(xf, d); n = length; if(n <= 0) putstart(xf, d, 0); else n -= xf->sectsize; while(clust > 0){ next = getfat(xf, clust); if(n <= 0) putfat(xf, clust, 0); else n -= bp->clustsize*xf->sectsize; clust = next; } unmlock(bp); PLONG(d->length, length); dp->iclust = 0; dp->clust = 0; dp->p->flags |= BMOD; return 0; } void putdir(Dosdir *d, Dir *dp) { if(dp->mode != ~0){ if(dp->mode & 2) d->attr &= ~DRONLY; else d->attr |= DRONLY; if(dp->mode & DMEXCL) d->attr |= DSYSTEM; else d->attr &= ~DSYSTEM; } if(dp->mtime != ~0) puttime(d, dp->mtime); } /* * should extend this to deal with * creation and access dates */ void getdir(Xfs *xf, Dir *dp, Dosdir *d, vlong addr, int offset) { if(d == nil || addr == 0) panic("getdir on root"); dp->type = 0; dp->dev = 0; getname(dp->name, d); dp->qid.path = addr*(xf->sectsize/DOSDIRSIZE) + offset/DOSDIRSIZE; dp->qid.vers = 0; if(d->attr & DRONLY) dp->mode = 0444; else dp->mode = 0666; dp->atime = gtime(d); dp->mtime = dp->atime; dp->qid.type = QTFILE; if(d->attr & DDIR){ dp->qid.type = QTDIR; dp->mode |= DMDIR|0111; dp->length = 0; }else dp->length = GLONG(d->length); if(d->attr & DSYSTEM){ dp->mode |= DMEXCL; if(iscontig(xf, d)) dp->mode |= DMAPPEND; } dp->uid = "bill"; dp->muid = "bill"; dp->gid = "trog"; } void getname(char *p, Dosdir *d) { int c, i; for(i=0; i<8; i++){ c = d->name[i]; if(c == '\0' || c == ' ') break; if(i == 0 && c == 0x05) c = 0xe5; *p++ = tolower(c); } for(i=0; i<3; i++){ c = d->ext[i]; if(c == '\0' || c == ' ') break; if(i == 0) *p++ = '.'; *p++ = tolower(c); } *p = 0; } static char* getnamerunes(char *dst, uchar *buf, int step) { int i; Rune r; char dbuf[DOSRUNE * UTFmax + 1], *d; d = dbuf; r = 1; for(i = 1; r && i < 11; i += 2){ r = buf[i] | (buf[i+1] << 8); d += runetochar(d, &r); } for(i = 14; r && i < 26; i += 2){ r = buf[i] | (buf[i+1] << 8); d += runetochar(d, &r); } for(i = 28; r && i < 32; i += 2){ r = buf[i] | (buf[i+1] << 8); d += runetochar(d, &r); } if(step == 1) dst -= d - dbuf; memmove(dst, dbuf, d - dbuf); if(step == -1){ dst += d - dbuf; *dst = '\0'; } return dst; } char* getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step) { /* * validation checks to make sure we're * making up a consistent name */ if(buf[11] != 0xf || buf[12] != 0){ *islong = 0; return nil; } if(step == 1){ if((buf[0] & 0xc0) == 0x40){ *islong = buf[0] & 0x3f; *sum = buf[13]; d = dbuf + DOSNAMELEN; *--d = '\0'; }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){ *islong = buf[0]; }else{ *islong = 0; return nil; } }else{ if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){ *islong = buf[0] & 0x3f; if(buf[0] & 0x40) *sum = -1; }else{ *islong = 0; *sum = -1; return nil; } } if(*islong > 20){ *islong = 0; *sum = -1; return nil; } return getnamerunes(d, buf, step); } void putname(char *p, Dosdir *d) { int i; memset(d->name, ' ', sizeof d->name+sizeof d->ext); for(i=0; i<sizeof d->name; i++){ if(*p == 0 || *p == '.') break; d->name[i] = toupper(*p++); } p = strrchr(p, '.'); if(p){ for(i=0; i<sizeof d->ext; i++){ if(*++p == 0) break; d->ext[i] = toupper(*p); } } } static void putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum) { Rune r; Dosdir ds; int i, j; memset(&ds, 0xff, sizeof ds); ds.attr = 0xf; ds.reserved[0] = 0; ds.reserved[1] = sum; ds.start[0] = 0; ds.start[1] = 0; if(first) ds.name[0] = 0x40 | curslot; else ds.name[0] = curslot; memmove(slot, &ds, sizeof ds); j = (curslot-1) * DOSRUNE; for(i = 1; i < 11; i += 2){ r = longname[j++]; slot[i] = r; slot[i+1] = r >> 8; if(r == 0) return; } for(i = 14; i < 26; i += 2){ r = longname[j++]; slot[i] = r; slot[i+1] = r >> 8; if(r == 0) return; } for(i = 28; i < 32; i += 2){ r = longname[j++]; slot[i] = r; slot[i+1] = r >> 8; if(r == 0) return; } } int aliassum(Dosdir *d) { int i, sum; if(d == nil) return -1; sum = 0; for(i = 0; i < 11; i++) sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i]; return sum & 0xff; } int putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13]) { Dosdir tmpd; Rune longname[DOSNAMELEN+1]; int i, first, sum, nds, len; /* calculate checksum */ putname(sname, &tmpd); sum = aliassum(&tmpd); first = 1; len = utftorunes(longname, name, DOSNAMELEN); if(chatty){ chat("utftorunes %s =", name); for(i=0; i<len; i++) chat(" %.4X", longname[i]); chat("\n"); } for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){ putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum); first = 0; ndp->offset += 32; if(ndp->offset == xf->sectsize){ chat("long name moving over sector boundary\n"); ndp->p->flags |= BMOD; putsect(ndp->p); ndp->p = nil; /* * switch to the next cluster for a long entry * naddr should be set up correctly by searchdir */ ndp->prevaddr = ndp->addr; ndp->addr = ndp->naddr; ndp->naddr = -1; if(ndp->addr == -1) return -1; ndp->p = getsect(xf, ndp->addr); if(ndp->p == nil) return -1; ndp->offset = 0; ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset]; } } return 0; } long getfat(Xfs *xf, long n) { Dosbpb *bp = xf->ptr; Iosect *p; ulong k, o; vlong sect; int fb; if(n < FATRESRV || n >= bp->fatclusters) return -1; fb = bp->fatbits; k = (fb * n) >> 3; if(k >= bp->fatsize*xf->sectsize) panic("getfat"); sect = k/xf->sectsize + bp->fataddr; o = k%xf->sectsize; p = getsect(xf, sect); if(p == nil) return -1; k = p->iobuf[o++]; if(o >= xf->sectsize){ putsect(p); p = getsect(xf, sect+1); if(p == nil) return -1; o = 0; } k |= p->iobuf[o++]<<8; if(fb == 32){ /* fat32 is really fat28 */ k |= p->iobuf[o++] << 16; k |= (p->iobuf[o] & 0x0f) << 24; fb = 28; } putsect(p); if(fb == 12){ if(n&1) k >>= 4; else k &= 0xfff; } if(chatty > 1) chat("fat(%#lx)=%#lux...", n, k); /* * This is a very strange check for out of range. * As a concrete example, for a 16-bit FAT, * FFF8 through FFFF all signify ``end of cluster chain.'' * This generalizes to other-sized FATs. */ if(k >= (1UL << fb) - 8) return -1; return k; } void putfat(Xfs *xf, int n, ulong val) { Fatinfo *fi; Dosbpb *bp; Iosect *p; ulong k; vlong sect, esect; int o; bp = xf->ptr; if(n < FATRESRV || n >= bp->fatclusters) panic("putfat n=%d", n); k = (bp->fatbits * n) >> 3; if(k >= bp->fatsize*xf->sectsize) panic("putfat"); sect = k/xf->sectsize + bp->fataddr; esect = sect + bp->nfats * bp->fatsize; for(; sect<esect; sect+=bp->fatsize){ o = k%xf->sectsize; p = getsect(xf, sect); if(p == nil) continue; switch(bp->fatbits){ case 12: if(n&1){ p->iobuf[o] &= 0x0f; p->iobuf[o++] |= val<<4; if(o >= xf->sectsize){ p->flags |= BMOD; putsect(p); p = getsect(xf, sect+1); if(p == nil) continue; o = 0; } p->iobuf[o] = val>>4; }else{ p->iobuf[o++] = val; if(o >= xf->sectsize){ p->flags |= BMOD; putsect(p); p = getsect(xf, sect+1); if(p == nil) continue; o = 0; } p->iobuf[o] &= 0xf0; p->iobuf[o] |= (val>>8) & 0x0f; } break; case 16: p->iobuf[o++] = val; p->iobuf[o] = val>>8; break; case 32: /* fat32 is really fat28 */ p->iobuf[o++] = val; p->iobuf[o++] = val>>8; p->iobuf[o++] = val>>16; p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f); break; default: panic("putfat fatbits"); } p->flags |= BMOD; putsect(p); } if(val == 0) bp->freeclusters++; else bp->freeclusters--; if(bp->fatinfo){ p = getsect(xf, bp->fatinfo); if(p != nil){ fi = (Fatinfo*)p->iobuf; PLONG(fi->nextfree, bp->freeptr); PLONG(fi->freeclust, bp->freeclusters); p->flags |= BMOD; putsect(p); } } } /* * Contiguous falloc; if we can, use lastclust+1. * Otherwise, move the file to get some space. * If there just isn't enough contiguous space * anywhere on disk, fail. */ int cfalloc(Xfile *f) { int l; if((l=makecontig(f, 8)) >= 0) return l; return makecontig(f, 1); } /* * Check whether a file is contiguous. */ int iscontig(Xfs *xf, Dosdir *d) { long clust, next; clust = getstart(xf, d); if(clust <= 0) return 1; for(;;) { next = getfat(xf, clust); if(next < 0) return 1; if(next != clust+1) return 0; clust = next; } } /* * Make a file contiguous, with nextra clusters of * free space after it for later expansion. * Return the number of the first new cluster. */ int makecontig(Xfile *f, int nextra) { Dosbpb *bp; Dosdir *d; Dosptr *dp; Xfs *xf; Iosect *wp, *rp; long clust, next, last, start, rclust, wclust, eclust, ostart; int isok, i, n, nclust, nrun; vlong rs, ws; xf = f->xf; bp = xf->ptr; dp = f->ptr; d = dp->d; isok = 1; nclust = 0; clust = fileclust(f, 0, 0); chat("clust %#lux", clust); if(clust != -1) { for(;;) { nclust++; chat("."); next = getfat(xf, clust); if(next <= 0) break; if(next != clust+1) isok = 0; clust = next; } } chat("nclust %d\n", nclust); if(isok && clust != -1) { eclust = clust+1; /* eclust = first cluster past file */ assert(eclust == fileclust(f, 0, 0)+nclust); for(i=0; i<nextra; i++) if(getfat(xf, eclust+i) != 0) break; if(i == nextra) { /* they were all free */ chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1)); assert(getfat(xf, eclust-1) == 0xffffffff); putfat(xf, eclust-1, eclust); putfat(xf, eclust, 0xffffffff); bp->freeptr = clust+1; /* to help keep the blocks free */ return eclust; } } /* need to search for nclust+nextra contiguous free blocks */ last = -1; n = bp->freeptr; nrun = 0; for(;;){ if(getfat(xf, n) == 0) { if(last+1 == n) nrun++; else nrun = 1; if(nrun >= nclust+nextra) break; last = n; } if(++n >= bp->fatclusters) n = FATRESRV; if(n == bp->freeptr) { errno = Econtig; return -1; } } bp->freeptr = n+1; /* copy old data over */ start = n+1 - nrun; /* sanity check */ for(i=0; i<nclust+nextra; i++) assert(getfat(xf, start+i) == 0); chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust); wclust = start; for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){ rs = clust2sect(bp, rclust); ws = clust2sect(bp, wclust); for(i=0; i<bp->clustsize; i++, rs++, ws++){ rp = getsect(xf, rs); if(rp == nil) return -1; wp = getosect(xf, ws); assert(wp != nil); memmove(wp->iobuf, rp->iobuf, xf->sectsize); wp->flags = BMOD; putsect(rp); putsect(wp); } chat("move cluster %#lx -> %#lx...", rclust, wclust); next = getfat(xf, rclust); putfat(xf, wclust, wclust+1); wclust++; } /* now wclust points at the first new cluster; chain it in */ chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust); assert(wclust == start+nclust); putfat(xf, wclust, 0xffffffff); /* end of file */ /* update directory entry to point at new start */ ostart = fileclust(f, 0, 0); putstart(xf, d, start); /* check our work */ i = 0; clust = fileclust(f, 0, 0); if(clust != -1) { for(;;) { i++; next = getfat(xf, clust); if(next <= 0) break; assert(next == clust+1); clust = next; } } chat("chain check: len %d\n", i); assert(i == nclust+1); /* succeeded; remove old chain. */ for(rclust = ostart; rclust > 0; rclust = next){ next = getfat(xf, rclust); putfat(xf, rclust, 0); /* free cluster */ } return start+nclust; } int falloc(Xfs *xf) { Dosbpb *bp = xf->ptr; Iosect *p; int n, i; vlong k; n = bp->freeptr; for(;;){ if(getfat(xf, n) == 0) break; if(++n >= bp->fatclusters) n = FATRESRV; if(n == bp->freeptr) return -1; } bp->freeptr = n+1; if(bp->freeptr >= bp->fatclusters) bp->freeptr = FATRESRV; putfat(xf, n, 0xffffffff); k = clust2sect(bp, n); for(i=0; i<bp->clustsize; i++){ p = getosect(xf, k+i); memset(p->iobuf, 0, xf->sectsize); p->flags = BMOD; putsect(p); } return n; } void ffree(Xfs *xf, long start) { putfat(xf, start, 0); } vlong clust2sect(Dosbpb *bp, long clust) { return bp->dataaddr + ((vlong)(clust - FATRESRV) * bp->clustsize); } long sect2clust(Dosbpb *bp, vlong sect) { return ((sect - bp->dataaddr) / bp->clustsize) + FATRESRV; } void puttime(Dosdir *d, long s) { Tm *t; ushort x; if(s == 0) s = time(0); t = localtime(s); x = (t->hour<<11) | (t->min<<5) | (t->sec>>1); PSHORT(d->time, x); x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; PSHORT(d->date, x); PSHORT(d->adate, x); } long gtime(Dosdir *dp) { Tm tm; int i; i = GSHORT(dp->time); memset(&tm, 0, sizeof(tm)); tm.hour = i >> 11; tm.min = (i >> 5) & 63; tm.sec = (i & 31) << 1; i = GSHORT(dp->date); tm.year = 80 + (i >> 9); tm.mon = ((i >> 5) & 15) - 1; tm.mday = i & 31; tm.zone[0] = '\0'; tm.tzoff = 0; tm.yday = 0; return tm2sec(&tm); } /* * structure dumps for debugging */ void bootdump(int fd, Dosboot *b) { Biobuf bp; Binit(&bp, fd, OWRITE); Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", b->magic[0], b->magic[1], b->magic[2]); Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); Bprint(&bp, "clustsize: %d\n", b->clustsize); Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); Bprint(&bp, "nfats: %d\n", b->nfats); Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden)); Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize)); Bprint(&bp, "driveno: %d\n", b->driveno); Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0); Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig); Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid)); Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label); Bterm(&bp); } void bootdump32(int fd, Dosboot32 *b) { Biobuf bp; Binit(&bp, fd, OWRITE); Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", b->magic[0], b->magic[1], b->magic[2]); Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); Bprint(&bp, "clustsize: %d\n", b->clustsize); Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); Bprint(&bp, "nfats: %d\n", b->nfats); Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden)); Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize)); Bprint(&bp, "fatsize32: %lud\n", GLONG(b->fatsize32)); Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags)); Bprint(&bp, "version: %d\n", GSHORT(b->version1)); Bprint(&bp, "rootstart: %lud\n", GLONG(b->rootstart)); Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec)); Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot)); Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n", b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3], b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7], b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]); Bterm(&bp); } void bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32) { Fatinfo *fi; Iosect *p1; int fisec, bsec, res; fprint(fd, "\nfat32\n"); bootdump32(fd, b32); res = GSHORT(b32->nresrv); bsec = GSHORT(b32->backupboot); if(bsec != 0 && bsec != 0xffff){ if(bsec >= res) fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res); else { p1 = getsect(xf, bsec); if(p1 == nil) fprint(fd, "\ncouldn't get backup boot sector: %r\n"); else{ fprint(fd, "\nbackup boot\n"); bootdump32(fd, (Dosboot32*)p1->iobuf); putsect(p1); } } } fisec = GSHORT(b32->infospec); if(fisec != 0 && fisec != 0xffff){ if(fisec >= res) fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res); else { p1 = getsect(xf, fisec); if(p1 == nil) fprint(fd, "\ncouldn't get fat info sector: %r\n"); else{ fprint(fd, "\nfat info %d\n", fisec); fi = (Fatinfo*)p1->iobuf; fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1); fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG); fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust)); fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree)); fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8)); putsect(p1); } } } } void dirdump(void *vdbuf) { static char attrchar[] = "rhsvda67"; Dosdir *d; char *name, namebuf[DOSNAMELEN]; char buf[128], *s, *ebuf; uchar *dbuf; int i; if(!chatty) return; d = vdbuf; ebuf = buf + sizeof(buf); if((d->attr & 0xf) == 0xf){ dbuf = vdbuf; name = namebuf + DOSNAMELEN; *--name = '\0'; name = getnamerunes(name, dbuf, 1); seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start)); }else{ s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext); for(i=7; i>=0; i--) *s++ = d->attr&(1<<i) ? attrchar[i] : '-'; i = GSHORT(d->time); s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); i = GSHORT(d->date); s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); i = GSHORT(d->ctime); s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d+%d", i>>11, (i>>5)&63, (i&31)<<1, (int)d->ctimetenth); i = GSHORT(d->cdate); s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); i = GSHORT(d->adate); s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); seprint(s, ebuf, " %d %lud", GSHORT(d->start), GLONG(d->length)); } chat("%s\n", buf); } int cistrcmp(char *s1, char *s2) { int c1, c2; while(*s1){ c1 = *s1++; c2 = *s2++; if(c1 >= 'A' && c1 <= 'Z') c1 -= 'A' - 'a'; if(c2 >= 'A' && c2 <= 'Z') c2 -= 'A' - 'a'; if(c1 != c2) return c1 - c2; } return -*s2; } int utftorunes(Rune *rr, char *s, int n) { Rune *r, *re; int c; r = rr; re = r + n - 1; while(c = (uchar)*s){ if(c < Runeself){ *r = c; s++; }else s += chartorune(r, s); r++; if(r >= re) break; } *r = 0; return r - rr; }