10 static uchar isdos[256];
16 * When dynamic disc managers move the disc partition,
17 * they make it start with 0xE9.
23 * Check if the jump displacement (magic[1]) is too short for a FAT.
25 * check now omitted due to digital cameras that use a 0 jump.
26 * the ecma-107 standard says this is okay and that interoperable fat
27 * implementations shouldn't assume this:
28 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf,
31 if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */)
34 fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]);
51 for(i = 'a'; i <= 'z'; i++)
53 for(i = 'A'; i <= 'Z'; i++)
55 for(i = '0'; i <= '9'; i++)
79 b = (Dosboot*)p->iobuf;
80 if(b->clustsize == 0 || isdosfs(p->iobuf) == 0){
85 bp = malloc(sizeof(Dosbpb));
86 memset(bp, 0, sizeof(Dosbpb)); /* clear lock */
90 bp->sectsize = GSHORT(b->sectsize);
91 bp->clustsize = b->clustsize;
92 bp->nresrv = GSHORT(b->nresrv);
94 bp->rootsize = GSHORT(b->rootsize);
95 bp->volsize = GSHORT(b->volsize);
97 bp->volsize = GLONG(b->bigvolsize);
98 bp->mediadesc = b->mediadesc;
99 bp->fatsize = GSHORT(b->fatsize);
100 bp->fataddr = GSHORT(b->nresrv);
104 if(bp->fatsize == 0){ /* is FAT32 */
106 bootsecdump32(2, xf, (Dosboot32*)b);
109 bp->fatsize = GLONG(b32->fatsize32);
110 if(bp->fatsize == 0){
113 fprint(2, "fatsize 0\n");
116 bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize;
118 bp->rootstart = GLONG(b32->rootstart);
121 * disable fat mirroring?
123 extflags = GSHORT(b32->extflags);
124 if(extflags & 0x0080){
125 for(i = 0; i < 4; i++){
126 if(extflags & (1 << i)){
127 bp->fataddr += i * bp->fatsize;
137 bp->freeptr = FATRESRV;
138 fisec = GSHORT(b32->infospec);
139 if(fisec != 0 && fisec < GSHORT(b32->nresrv)){
140 p1 = getsect(xf, fisec);
142 fi = (Fatinfo*)p1->iobuf;
143 if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){
145 bp->freeptr = GLONG(fi->nextfree);
146 bp->freeclusters = GLONG(fi->freeclust);
147 chat("fat info: %ld free clusters, next free %ld\n", bp->freeclusters, bp->freeptr);
155 bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
157 i = bp->rootsize*DOSDIRSIZE + bp->sectsize-1;
159 bp->dataaddr = bp->rootaddr + i;
160 bp->freeptr = FATRESRV;
162 bp->fatclusters = FATRESRV+(bp->volsize - bp->dataaddr)/bp->clustsize;
166 else if(bp->fatclusters < 4087)
171 chat("fatbits=%d (%d clusters)...", bp->fatbits, bp->fatclusters);
172 for(i=0; i<b->nfats; i++)
173 chat("fat %d: %ld...", i, bp->fataddr+i*bp->fatsize);
174 chat("root: %ld...", bp->rootaddr);
175 chat("data: %ld...", bp->dataaddr);
181 * initialize f to the root directory
182 * this file has no Dosdir entry,
183 * so we special case it all over.
191 memset(dp, 0, sizeof(Dosptr));
210 p = getsect(f->xf, dp->addr);
215 * we could also make up a Dosdir for the root
218 if(!isroot(dp->addr)){
219 if(f->qid.path != QIDPATH(dp)){
220 chat("qid mismatch f=%#llux d=%#lux...", f->qid.path, QIDPATH(dp));
225 dp->d = (Dosdir *)&p->iobuf[dp->offset];
245 getstart(Xfs *xf, Dosdir *d)
249 start = GSHORT(d->start);
251 start |= GSHORT(d->hstart)<<16;
256 putstart(Xfs *xf, Dosdir *d, long start)
258 PSHORT(d->start, start);
260 PSHORT(d->hstart, start>>16);
264 * return the disk cluster for the iclust cluster in f
267 fileclust(Xfile *f, long iclust, int cflag)
272 long start, clust, nskip, next;
280 * asking for the cluster of the root directory
281 * is not a well-formed question, since the root directory
282 * does not begin on a cluster boundary.
284 if(!f->xf->isfat32 && isroot(dp->addr))
287 if(f->xf->isfat32 && isroot(dp->addr)){
288 start = bp->rootstart;
290 start = getstart(f->xf, d);
295 start = falloc(f->xf);
300 putstart(f->xf, d, start);
301 dp->p->flags |= BMOD;
305 if(dp->clust == 0 || iclust < dp->iclust){
310 nskip = iclust - dp->iclust;
312 if(chatty > 1 && nskip > 0)
313 chat("clust %#lx, skip %ld...", clust, nskip);
319 next = getfat(f->xf, clust);
321 chat("->%#lx", next);
327 if(d && (d->attr&DSYSTEM)){
331 /* cfalloc will call putfat for us, since clust may change */
333 next = falloc(f->xf);
336 putfat(f->xf, clust, next);
347 chat(" clust(%#lx)=%#lx...", iclust, clust);
352 * return the disk sector for the isect disk sector in f
355 fileaddr(Xfile *f, long isect, int cflag)
363 if(!f->xf->isfat32 && isroot(dp->addr)){
364 if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
366 return bp->rootaddr + isect;
368 clust = fileclust(f, isect/bp->clustsize, cflag);
372 return clust2sect(bp, clust) + isect%bp->clustsize;
386 if(c == ':' && trspaces)
393 * classify the file name as one of
394 * Invalid - contains a bad character
395 * Short - short valid 8.3 name, no lowercase letters
396 * ShortLower - short valid 8.3 name except for lowercase letters
400 classifyname(char *buf)
403 int c, isextended, is8dot3, islower, ndot;
410 while(c = (uchar)*p){
411 if(c&0x80) /* UTF8 */
416 }else if(strchr("+,:;=[] ", c))
420 if('a' <= c && c <= 'z')
425 is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
427 if(!isextended && is8dot3){
436 * make an alias for a valid long file name
439 mkalias(char *name, char *sname, int id)
442 char *s, *e, sid[10];
445 e = strrchr(name, '.');
447 e = strchr(name, '\0');
451 while(s < e && i < 6){
455 s += chartorune(&r, s);
458 v = snprint(sid, 10, "%d", id);
462 strcpy(&sname[i], sid);
468 panic("bad mkalias");
469 while(*e && i < esuf){
473 e += chartorune(&r, e);
475 if(sname[i-1] == '.')
481 * check for valid plan 9 names,
485 /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1,
486 /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1,
487 /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1,
488 /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1,
489 /* [' '] 1, let's try this -rsc */
498 if(*elem == ' ' && trspaces)
500 if(isfrog[*(uchar*)elem])
508 * look for a directory entry matching name
509 * always searches for long names which match a short name
512 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype)
518 char buf[261], *bname;
519 int isect, addr, o, addr1, addr2, prevaddr, prevaddr1, o1, islong, have, need, sum;
531 if(longtype!=Short && cflag)
532 need += (utflen(name) + DOSRUNE-1) / DOSRUNE;
534 memset(dp, 0, sizeof(Dosptr));
537 dp->paddr = ((Dosptr *)f->ptr)->addr;
538 dp->poffset = ((Dosptr *)f->ptr)->offset;
543 for(isect=0;; isect++){
545 addr = fileaddr(f, isect, cflag);
548 p = getsect(xf, addr);
551 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
552 d = (Dosdir *)&p->iobuf[o];
553 if(d->name[0] == 0x00){
554 chat("end dir(0)...");
560 * addr1 & o1 are the start of the dirs
561 * addr2 is the optional second cluster used if the long name
562 * entry does not fit within the addr1 cluster
564 * have tells us the number of contiguous free dirs
565 * starting at addr1.o1; need are necessary to hold the long name.
569 prevaddr1 = prevaddr;
572 if(addr2 < 0 && (bp->sectsize-o)/DOSDIRSIZE + have < need){
573 addr2 = fileaddr(f, isect+1, cflag);
582 dp->prevaddr = prevaddr1;
586 if(d->name[0] == DOSEMPTY){
588 fprint(2, "empty dir\n");
594 prevaddr1 = prevaddr;
596 if(addr2 == -1 && have >= need)
605 if((d->attr & 0xf) == 0xf){
606 bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1);
609 if(d->attr & DVLABEL){
613 if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){
618 if(cistrcmp(bname, name) != 0)
621 fprint(2, "found\n");
627 dp->prevaddr = prevaddr;
636 chat("end dir(1)...");
644 Dosbpb *bp = xf->ptr;
649 for(isect=0;; isect++){
650 addr = fileaddr(f, isect, 0);
653 p = getsect(xf, addr);
656 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
657 d = (Dosdir *)&p->iobuf[o];
658 if(d->name[0] == 0x00){
662 if(d->name[0] == DOSEMPTY)
664 if(d->name[0] == '.')
677 readdir(Xfile *f, void *vbuf, long offset, long count)
682 int isect, addr, o, islong, sum;
686 char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN];
698 for(isect=0;; isect++){
699 addr = fileaddr(f, isect, 0);
702 p = getsect(xf, addr);
705 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
706 d = (Dosdir *)&p->iobuf[o];
707 if(d->name[0] == 0x00){
711 if(d->name[0] == DOSEMPTY)
714 if(d->name[0] == '.'){
715 if(d->name[1] == ' ' || d->name[1] == 0)
717 if(d->name[1] == '.' &&
718 (d->name[2] == ' ' || d->name[2] == 0))
721 if((d->attr & 0xf) == 0xf){
722 name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1);
725 if(d->attr & DVLABEL){
730 getdir(xf, &dir, d, addr, o);
731 if(islong == 1 && nameok(name) && sum == aliassum(d))
734 n = convD2M(&dir, &buf[rcnt], count - rcnt);
736 if(n <= BIT16SZ){ /* no room for next entry */
758 * set up ndp for a directory's parent
759 * the hardest part is setting up paddr
762 walkup(Xfile *f, Dosptr *ndp)
768 long k, o, so, start, pstart, ppstart, st, ppclust;
772 memset(ndp, 0, sizeof(Dosptr));
775 ndp->addr = dp->paddr;
776 ndp->offset = dp->poffset;
778 chat("walkup: paddr=%#lx...", dp->paddr);
781 * root's paddr is always itself
783 if(isroot(dp->paddr))
787 * find the start of our parent's directory
789 p = getsect(f->xf, dp->paddr);
792 xd = (Dosdir *)&p->iobuf[dp->poffset];
794 start = getstart(f->xf, xd);
795 chat("start=%#lx...", start);
801 * verify that parent's . points to itself
803 p = getsect(f->xf, clust2sect(bp, start));
806 xd = (Dosdir *)p->iobuf;
808 st = getstart(f->xf, xd);
809 if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st)
813 * parent's .. is the next entry, and has start of parent's parent
817 if(xd->name[0] != '.' || xd->name[1] != '.')
819 pstart = getstart(f->xf, xd);
823 * we're done if parent is root
825 if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart)
829 * verify that parent's . points to itself
831 p = getsect(f->xf, clust2sect(bp, pstart));
833 chat("getsect %ld failed\n", pstart);
836 xd = (Dosdir *)p->iobuf;
838 st = getstart(f->xf, xd);
839 if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st)
843 * parent's parent's .. is the next entry, and has start of parent's parent's parent
847 if(xd->name[0] != '.' || xd->name[1] != '.')
849 ppstart = getstart(f->xf, xd);
853 * open parent's parent's parent, and walk through it until parent's parent is found
854 * need this to find parent's parent's addr and offset
857 if(f->xf->isfat32 && ppclust == 0){
858 ppclust = bp->rootstart;
859 chat("ppclust 0, resetting to rootstart\n");
861 k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr;
862 p = getsect(f->xf, k);
864 chat("getsect %ld failed\n", k);
867 xd = (Dosdir *)p->iobuf;
870 st = getstart(f->xf, xd);
871 if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st)
875 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
876 xd = (Dosdir *)&p->iobuf[o];
877 if(xd->name[0] == 0x00){
881 if(xd->name[0] == DOSEMPTY)
883 st = getstart(f->xf, xd);
888 if(so%bp->clustsize == 0){
890 ppclust = getfat(f->xf, ppclust);
893 chat("getfat %ld failed\n", ppclust);
897 k = clust2sect(bp, ppclust) +
900 if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
902 k = bp->rootaddr + so;
905 p = getsect(f->xf, k);
907 chat("getsect %ld failed\n", k);
924 readfile(Xfile *f, void *vbuf, long offset, long count)
927 Dosbpb *bp = xf->ptr;
930 int isect, addr, o, c;
936 length = GLONG(d->length);
940 if(offset+count >= length)
941 count = length - offset;
942 isect = offset/bp->sectsize;
943 o = offset%bp->sectsize;
945 addr = fileaddr(f, isect++, 0);
948 c = bp->sectsize - o;
951 p = getsect(xf, addr);
954 memmove(&buf[rcnt], &p->iobuf[o], c);
964 writefile(Xfile *f, void *vbuf, long offset, long count)
967 Dosbpb *bp = xf->ptr;
970 int isect, addr = 0, o, c;
973 long length, rcnt = 0, dlen;
976 isect = offset/bp->sectsize;
977 o = offset%bp->sectsize;
979 addr = fileaddr(f, isect++, 1);
982 c = bp->sectsize - o;
985 if(c == bp->sectsize){
986 p = getosect(xf, addr);
989 p = getsect(xf, addr);
993 memmove(&p->iobuf[o], &buf[rcnt], c);
1000 if(rcnt <= 0 && addr < 0)
1003 dlen = GLONG(d->length);
1005 length = offset+rcnt;
1006 else if(dp->addr && dp->clust){
1007 c = bp->clustsize*bp->sectsize;
1008 if(dp->iclust > (dlen+c-1)/c)
1009 length = c*dp->iclust;
1012 PLONG(d->length, length);
1014 dp->p->flags |= BMOD;
1019 truncfile(Xfile *f, long length)
1022 Dosbpb *bp = xf->ptr;
1023 Dosptr *dp = f->ptr;
1025 long clust, next, n;
1028 clust = getstart(f->xf, d);
1031 putstart(f->xf, d, 0);
1035 next = getfat(xf, clust);
1037 putfat(xf, clust, 0);
1039 n -= bp->clustsize*bp->sectsize;
1043 PLONG(d->length, length);
1046 dp->p->flags |= BMOD;
1051 putdir(Dosdir *d, Dir *dp)
1058 if(dp->mode & DMEXCL)
1061 d->attr &= ~DSYSTEM;
1064 puttime(d, dp->mtime);
1068 * should extend this to deal with
1069 * creation and access dates
1072 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset)
1074 if(d == nil || addr == 0)
1075 panic("getdir on root");
1078 getname(dp->name, d);
1080 dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1084 if(d->attr & DRONLY)
1088 dp->atime = gtime(d);
1089 dp->mtime = dp->atime;
1090 dp->qid.type = QTFILE;
1092 dp->qid.type = QTDIR;
1093 dp->mode |= DMDIR|0111;
1096 dp->length = GLONG(d->length);
1097 if(d->attr & DSYSTEM){
1099 if(iscontig(xfs, d))
1100 dp->mode |= DMAPPEND;
1109 getname(char *p, Dosdir *d)
1115 if(c == '\0' || c == ' ')
1117 if(i == 0 && c == 0x05)
1123 if(c == '\0' || c == ' ')
1133 getnamerunes(char *dst, uchar *buf, int step)
1137 char dbuf[DOSRUNE * UTFmax + 1], *d;
1141 for(i = 1; r && i < 11; i += 2){
1142 r = buf[i] | (buf[i+1] << 8);
1143 d += runetochar(d, &r);
1145 for(i = 14; r && i < 26; i += 2){
1146 r = buf[i] | (buf[i+1] << 8);
1147 d += runetochar(d, &r);
1149 for(i = 28; r && i < 32; i += 2){
1150 r = buf[i] | (buf[i+1] << 8);
1151 d += runetochar(d, &r);
1157 memmove(dst, dbuf, d - dbuf);
1168 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1171 * validation checks to make sure we're
1172 * making up a consistent name
1174 if(buf[11] != 0xf || buf[12] != 0){
1179 if((buf[0] & 0xc0) == 0x40){
1180 *islong = buf[0] & 0x3f;
1182 d = dbuf + DOSNAMELEN;
1184 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1191 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1192 *islong = buf[0] & 0x3f;
1207 return getnamerunes(d, buf, step);
1211 putname(char *p, Dosdir *d)
1215 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1216 for(i=0; i<sizeof d->name; i++){
1217 if(*p == 0 || *p == '.')
1219 d->name[i] = toupper(*p++);
1221 p = strrchr(p, '.');
1223 for(i=0; i<sizeof d->ext; i++){
1226 d->ext[i] = toupper(*p);
1232 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1238 memset(&ds, 0xff, sizeof ds);
1241 ds.reserved[1] = sum;
1245 ds.name[0] = 0x40 | curslot;
1247 ds.name[0] = curslot;
1248 memmove(slot, &ds, sizeof ds);
1250 j = (curslot-1) * DOSRUNE;
1252 for(i = 1; i < 11; i += 2){
1259 for(i = 14; i < 26; i += 2){
1266 for(i = 28; i < 32; i += 2){
1283 for(i = 0; i < 11; i++)
1284 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1289 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1293 Rune longname[DOSNAMELEN+1];
1294 int i, first, sum, nds, len;
1296 /* calculate checksum */
1297 putname(sname, &tmpd);
1298 sum = aliassum(&tmpd);
1302 len = utftorunes(longname, name, DOSNAMELEN);
1304 chat("utftorunes %s =", name);
1305 for(i=0; i<len; i++)
1306 chat(" %.4X", longname[i]);
1309 for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1310 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1313 if(ndp->offset == bp->sectsize){
1314 chat("long name moving over sector boundary\n");
1315 ndp->p->flags |= BMOD;
1320 * switch to the next cluster for a long entry
1321 * naddr should be set up correctly by searchdir
1323 ndp->prevaddr = ndp->addr;
1324 ndp->addr = ndp->naddr;
1328 ndp->p = getsect(xf, ndp->addr);
1332 ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1339 getfat(Xfs *xf, int n)
1341 Dosbpb *bp = xf->ptr;
1346 if(n < FATRESRV || n >= bp->fatclusters)
1350 if(k >= bp->fatsize*bp->sectsize)
1352 sect = k/bp->sectsize + bp->fataddr;
1354 p = getsect(xf, sect);
1358 if(o >= bp->sectsize){
1360 p = getsect(xf, sect+1);
1365 k |= p->iobuf[o++]<<8;
1367 /* fat32 is really fat28 */
1368 k |= p->iobuf[o++] << 16;
1369 k |= (p->iobuf[o] & 0x0f) << 24;
1380 chat("fat(%#x)=%#lx...", n, k);
1383 * This is a very strange check for out of range.
1384 * As a concrete example, for a 16-bit FAT,
1385 * FFF8 through FFFF all signify ``end of cluster chain.''
1386 * This generalizes to other-sized FATs.
1388 if(k >= (1 << fb) - 8)
1395 putfat(Xfs *xf, int n, ulong val)
1400 ulong k, sect, esect;
1404 if(n < FATRESRV || n >= bp->fatclusters)
1405 panic("putfat n=%d", n);
1406 k = (bp->fatbits * n) >> 3;
1407 if(k >= bp->fatsize*bp->sectsize)
1409 sect = k/bp->sectsize + bp->fataddr;
1410 esect = sect + bp->nfats * bp->fatsize;
1411 for(; sect<esect; sect+=bp->fatsize){
1413 p = getsect(xf, sect);
1416 switch(bp->fatbits){
1419 p->iobuf[o] &= 0x0f;
1420 p->iobuf[o++] |= val<<4;
1421 if(o >= bp->sectsize){
1424 p = getsect(xf, sect+1);
1429 p->iobuf[o] = val>>4;
1431 p->iobuf[o++] = val;
1432 if(o >= bp->sectsize){
1435 p = getsect(xf, sect+1);
1440 p->iobuf[o] &= 0xf0;
1441 p->iobuf[o] |= (val>>8) & 0x0f;
1445 p->iobuf[o++] = val;
1446 p->iobuf[o] = val>>8;
1448 case 32: /* fat32 is really fat28 */
1449 p->iobuf[o++] = val;
1450 p->iobuf[o++] = val>>8;
1451 p->iobuf[o++] = val>>16;
1452 p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f);
1455 panic("putfat fatbits");
1467 p = getsect(xf, bp->fatinfo);
1469 fi = (Fatinfo*)p->iobuf;
1470 PLONG(fi->nextfree, bp->freeptr);
1471 PLONG(fi->freeclust, bp->freeclusters);
1479 * Contiguous falloc; if we can, use lastclust+1.
1480 * Otherwise, move the file to get some space.
1481 * If there just isn't enough contiguous space
1482 * anywhere on disk, fail.
1489 if((l=makecontig(f, 8)) >= 0)
1491 return makecontig(f, 1);
1495 * Check whether a file is contiguous.
1498 iscontig(Xfs *xf, Dosdir *d)
1502 clust = getstart(xf, d);
1507 next = getfat(xf, clust);
1517 * Make a file contiguous, with nextra clusters of
1518 * free space after it for later expansion.
1519 * Return the number of the first new cluster.
1522 makecontig(Xfile *f, int nextra)
1529 long clust, next, last, start, rclust, wclust, eclust, ostart;
1530 int isok, i, n, nclust, nrun, rs, ws;
1539 clust = fileclust(f, 0, 0);
1540 chat("clust %#lux", clust);
1545 next = getfat(xf, clust);
1553 chat("nclust %d\n", nclust);
1555 if(isok && clust != -1) {
1556 eclust = clust+1; /* eclust = first cluster past file */
1557 assert(eclust == fileclust(f, 0, 0)+nclust);
1558 for(i=0; i<nextra; i++)
1559 if(getfat(xf, eclust+i) != 0)
1561 if(i == nextra) { /* they were all free */
1562 chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1));
1563 assert(getfat(xf, eclust-1) == 0xffffffff);
1564 putfat(xf, eclust-1, eclust);
1565 putfat(xf, eclust, 0xffffffff);
1566 bp->freeptr = clust+1; /* to help keep the blocks free */
1571 /* need to search for nclust+nextra contiguous free blocks */
1576 if(getfat(xf, n) == 0) {
1581 if(nrun >= nclust+nextra)
1585 if(++n >= bp->fatclusters)
1587 if(n == bp->freeptr) {
1594 /* copy old data over */
1598 for(i=0; i<nclust+nextra; i++)
1599 assert(getfat(xf, start+i) == 0);
1601 chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1604 for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){
1605 rs = clust2sect(bp, rclust);
1606 ws = clust2sect(bp, wclust);
1607 for(i=0; i<bp->clustsize; i++, rs++, ws++){
1608 rp = getsect(xf, rs);
1611 wp = getosect(xf, ws);
1613 memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1618 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1619 next = getfat(xf, rclust);
1620 putfat(xf, wclust, wclust+1);
1624 /* now wclust points at the first new cluster; chain it in */
1625 chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust);
1626 assert(wclust == start+nclust);
1627 putfat(xf, wclust, 0xffffffff); /* end of file */
1629 /* update directory entry to point at new start */
1630 ostart = fileclust(f, 0, 0);
1631 putstart(xf, d, start);
1633 /* check our work */
1635 clust = fileclust(f, 0, 0);
1639 next = getfat(xf, clust);
1642 assert(next == clust+1);
1646 chat("chain check: len %d\n", i);
1647 assert(i == nclust+1);
1649 /* succeeded; remove old chain. */
1650 for(rclust = ostart; rclust > 0; rclust = next){
1651 next = getfat(xf, rclust);
1652 putfat(xf, rclust, 0); /* free cluster */
1655 return start+nclust;
1661 Dosbpb *bp = xf->ptr;
1667 if(getfat(xf, n) == 0)
1669 if(++n >= bp->fatclusters)
1671 if(n == bp->freeptr)
1675 if(bp->freeptr >= bp->fatclusters)
1676 bp->freeptr = FATRESRV;
1677 putfat(xf, n, 0xffffffff);
1678 k = clust2sect(bp, n);
1679 for(i=0; i<bp->clustsize; i++){
1680 p = getosect(xf, k+i);
1681 memset(p->iobuf, 0, bp->sectsize);
1689 ffree(Xfs *xf, long start)
1691 putfat(xf, start, 0);
1695 clust2sect(Dosbpb *bp, long clust)
1697 return bp->dataaddr + (clust - FATRESRV) * bp->clustsize;
1701 sect2clust(Dosbpb *bp, long sect)
1705 c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV;
1706 assert(sect == clust2sect(bp, c));
1711 puttime(Dosdir *d, long s)
1720 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1722 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1732 i = GSHORT(dp->time);
1734 tm.min = (i >> 5) & 63;
1735 tm.sec = (i & 31) << 1;
1736 i = GSHORT(dp->date);
1737 tm.year = 80 + (i >> 9);
1738 tm.mon = ((i >> 5) & 15) - 1;
1748 * structure dumps for debugging
1751 bootdump(int fd, Dosboot *b)
1755 Binit(&bp, fd, OWRITE);
1756 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1757 b->magic[0], b->magic[1], b->magic[2]);
1758 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1759 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1760 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1761 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1762 Bprint(&bp, "nfats: %d\n", b->nfats);
1763 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1764 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1765 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1766 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1767 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1768 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1769 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1770 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1771 Bprint(&bp, "driveno: %d\n", b->driveno);
1772 Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0);
1773 Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig);
1774 Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid));
1775 Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label);
1780 bootdump32(int fd, Dosboot32 *b)
1784 Binit(&bp, fd, OWRITE);
1785 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1786 b->magic[0], b->magic[1], b->magic[2]);
1787 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1788 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1789 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1790 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1791 Bprint(&bp, "nfats: %d\n", b->nfats);
1792 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1793 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1794 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1795 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1796 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1797 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1798 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1799 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1800 Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32));
1801 Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
1802 Bprint(&bp, "version: %d\n", GSHORT(b->version1));
1803 Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart));
1804 Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec));
1805 Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot));
1806 Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n",
1807 b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3],
1808 b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7],
1809 b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]);
1814 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1818 int fisec, bsec, res;
1820 fprint(fd, "\nfat32\n");
1821 bootdump32(fd, b32);
1822 res = GSHORT(b32->nresrv);
1823 bsec = GSHORT(b32->backupboot);
1824 if(bsec < res && bsec != 0){
1825 p1 = getsect(xf, bsec);
1827 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1829 fprint(fd, "\nbackup boot\n");
1830 bootdump32(fd, (Dosboot32*)p1->iobuf);
1833 }else if(bsec != 0xffff)
1834 fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1835 fisec = GSHORT(b32->infospec);
1836 if(fisec < res && fisec != 0){
1837 p1 = getsect(xf, fisec);
1839 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1841 fprint(fd, "\nfat info %d\n", fisec);
1842 fi = (Fatinfo*)p1->iobuf;
1843 fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1);
1844 fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG);
1845 fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust));
1846 fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree));
1847 fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8));
1850 }else if(fisec != 0xffff)
1851 fprint(2, "bad fat info sector: %d reserved %d\n", bsec, res);
1855 dirdump(void *vdbuf)
1857 static char attrchar[] = "rhsvda67";
1859 char *name, namebuf[DOSNAMELEN];
1860 char buf[128], *s, *ebuf;
1869 ebuf = buf + sizeof(buf);
1872 name = namebuf + DOSNAMELEN;
1874 name = getnamerunes(name, dbuf, 1);
1875 seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start));
1877 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1879 *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1881 i = GSHORT(d->time);
1882 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1883 i = GSHORT(d->date);
1884 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1886 i = GSHORT(d->ctime);
1887 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1888 i = GSHORT(d->cdate);
1889 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1891 i = GSHORT(d->adate);
1892 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1894 seprint(s, ebuf, " %d %d", GSHORT(d->start), GSHORT(d->length));
1900 cistrcmp(char *s1, char *s2)
1908 if(c1 >= 'A' && c1 <= 'Z')
1911 if(c2 >= 'A' && c2 <= 'Z')
1921 utftorunes(Rune *rr, char *s, int n)
1928 while(c = (uchar)*s){
1933 s += chartorune(r, s);