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, vlong 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, vlong offset, long count)
927 Dosbpb *bp = xf->ptr;
930 int isect, addr, o, c;
935 if(offset >= MAXFILELEN)
937 if(offset+count > MAXFILELEN)
938 count = MAXFILELEN - offset;
941 length = GLONG(d->length);
945 if(offset+count >= length)
946 count = length - offset;
947 isect = offset/bp->sectsize;
948 o = offset%bp->sectsize;
950 addr = fileaddr(f, isect++, 0);
953 c = bp->sectsize - o;
956 p = getsect(xf, addr);
959 memmove(&buf[rcnt], &p->iobuf[o], c);
969 writefile(Xfile *f, void *vbuf, vlong offset, long count)
972 Dosbpb *bp = xf->ptr;
975 int isect, addr, o, c;
978 ulong length, rcnt, dlen;
980 if(offset >= MAXFILELEN)
982 if(offset+count > MAXFILELEN)
983 count = MAXFILELEN - offset;
988 isect = offset/bp->sectsize;
989 o = offset%bp->sectsize;
991 addr = fileaddr(f, isect++, 1);
994 c = bp->sectsize - o;
997 if(c == bp->sectsize){
998 p = getosect(xf, addr);
1001 p = getsect(xf, addr);
1005 memmove(&p->iobuf[o], &buf[rcnt], c);
1015 dlen = GLONG(d->length);
1017 length = offset+rcnt;
1018 else if(dp->addr && dp->clust){
1019 c = bp->clustsize*bp->sectsize;
1020 if(dp->iclust > (dlen+c-1)/c)
1021 length = c*dp->iclust;
1024 PLONG(d->length, length);
1026 dp->p->flags |= BMOD;
1031 truncfile(Xfile *f, vlong length)
1034 Dosbpb *bp = xf->ptr;
1035 Dosptr *dp = f->ptr;
1040 if(length > MAXFILELEN)
1044 clust = getstart(f->xf, d);
1047 putstart(f->xf, d, 0);
1051 next = getfat(xf, clust);
1053 putfat(xf, clust, 0);
1055 n -= bp->clustsize*bp->sectsize;
1059 PLONG(d->length, length);
1062 dp->p->flags |= BMOD;
1067 putdir(Dosdir *d, Dir *dp)
1074 if(dp->mode & DMEXCL)
1077 d->attr &= ~DSYSTEM;
1080 puttime(d, dp->mtime);
1084 * should extend this to deal with
1085 * creation and access dates
1088 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset)
1090 if(d == nil || addr == 0)
1091 panic("getdir on root");
1094 getname(dp->name, d);
1096 dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1100 if(d->attr & DRONLY)
1104 dp->atime = gtime(d);
1105 dp->mtime = dp->atime;
1106 dp->qid.type = QTFILE;
1108 dp->qid.type = QTDIR;
1109 dp->mode |= DMDIR|0111;
1112 dp->length = (ulong)GLONG(d->length);
1113 if(d->attr & DSYSTEM){
1115 if(iscontig(xfs, d))
1116 dp->mode |= DMAPPEND;
1125 getname(char *p, Dosdir *d)
1131 if(c == '\0' || c == ' ')
1133 if(i == 0 && c == 0x05)
1139 if(c == '\0' || c == ' ')
1149 getnamerunes(char *dst, uchar *buf, int step)
1153 char dbuf[DOSRUNE * UTFmax + 1], *d;
1157 for(i = 1; r && i < 11; i += 2){
1158 r = buf[i] | (buf[i+1] << 8);
1159 d += runetochar(d, &r);
1161 for(i = 14; r && i < 26; i += 2){
1162 r = buf[i] | (buf[i+1] << 8);
1163 d += runetochar(d, &r);
1165 for(i = 28; r && i < 32; i += 2){
1166 r = buf[i] | (buf[i+1] << 8);
1167 d += runetochar(d, &r);
1173 memmove(dst, dbuf, d - dbuf);
1184 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1187 * validation checks to make sure we're
1188 * making up a consistent name
1190 if(buf[11] != 0xf || buf[12] != 0){
1195 if((buf[0] & 0xc0) == 0x40){
1196 *islong = buf[0] & 0x3f;
1198 d = dbuf + DOSNAMELEN;
1200 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1207 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1208 *islong = buf[0] & 0x3f;
1223 return getnamerunes(d, buf, step);
1227 putname(char *p, Dosdir *d)
1231 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1232 for(i=0; i<sizeof d->name; i++){
1233 if(*p == 0 || *p == '.')
1235 d->name[i] = toupper(*p++);
1237 p = strrchr(p, '.');
1239 for(i=0; i<sizeof d->ext; i++){
1242 d->ext[i] = toupper(*p);
1248 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1254 memset(&ds, 0xff, sizeof ds);
1257 ds.reserved[1] = sum;
1261 ds.name[0] = 0x40 | curslot;
1263 ds.name[0] = curslot;
1264 memmove(slot, &ds, sizeof ds);
1266 j = (curslot-1) * DOSRUNE;
1268 for(i = 1; i < 11; i += 2){
1275 for(i = 14; i < 26; i += 2){
1282 for(i = 28; i < 32; i += 2){
1299 for(i = 0; i < 11; i++)
1300 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1305 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1309 Rune longname[DOSNAMELEN+1];
1310 int i, first, sum, nds, len;
1312 /* calculate checksum */
1313 putname(sname, &tmpd);
1314 sum = aliassum(&tmpd);
1318 len = utftorunes(longname, name, DOSNAMELEN);
1320 chat("utftorunes %s =", name);
1321 for(i=0; i<len; i++)
1322 chat(" %.4X", longname[i]);
1325 for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1326 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1329 if(ndp->offset == bp->sectsize){
1330 chat("long name moving over sector boundary\n");
1331 ndp->p->flags |= BMOD;
1336 * switch to the next cluster for a long entry
1337 * naddr should be set up correctly by searchdir
1339 ndp->prevaddr = ndp->addr;
1340 ndp->addr = ndp->naddr;
1344 ndp->p = getsect(xf, ndp->addr);
1348 ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1355 getfat(Xfs *xf, int n)
1357 Dosbpb *bp = xf->ptr;
1362 if(n < FATRESRV || n >= bp->fatclusters)
1366 if(k >= bp->fatsize*bp->sectsize)
1368 sect = k/bp->sectsize + bp->fataddr;
1370 p = getsect(xf, sect);
1374 if(o >= bp->sectsize){
1376 p = getsect(xf, sect+1);
1381 k |= p->iobuf[o++]<<8;
1383 /* fat32 is really fat28 */
1384 k |= p->iobuf[o++] << 16;
1385 k |= (p->iobuf[o] & 0x0f) << 24;
1396 chat("fat(%#x)=%#lx...", n, k);
1399 * This is a very strange check for out of range.
1400 * As a concrete example, for a 16-bit FAT,
1401 * FFF8 through FFFF all signify ``end of cluster chain.''
1402 * This generalizes to other-sized FATs.
1404 if(k >= (1 << fb) - 8)
1411 putfat(Xfs *xf, int n, ulong val)
1416 ulong k, sect, esect;
1420 if(n < FATRESRV || n >= bp->fatclusters)
1421 panic("putfat n=%d", n);
1422 k = (bp->fatbits * n) >> 3;
1423 if(k >= bp->fatsize*bp->sectsize)
1425 sect = k/bp->sectsize + bp->fataddr;
1426 esect = sect + bp->nfats * bp->fatsize;
1427 for(; sect<esect; sect+=bp->fatsize){
1429 p = getsect(xf, sect);
1432 switch(bp->fatbits){
1435 p->iobuf[o] &= 0x0f;
1436 p->iobuf[o++] |= val<<4;
1437 if(o >= bp->sectsize){
1440 p = getsect(xf, sect+1);
1445 p->iobuf[o] = val>>4;
1447 p->iobuf[o++] = val;
1448 if(o >= bp->sectsize){
1451 p = getsect(xf, sect+1);
1456 p->iobuf[o] &= 0xf0;
1457 p->iobuf[o] |= (val>>8) & 0x0f;
1461 p->iobuf[o++] = val;
1462 p->iobuf[o] = val>>8;
1464 case 32: /* fat32 is really fat28 */
1465 p->iobuf[o++] = val;
1466 p->iobuf[o++] = val>>8;
1467 p->iobuf[o++] = val>>16;
1468 p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f);
1471 panic("putfat fatbits");
1483 p = getsect(xf, bp->fatinfo);
1485 fi = (Fatinfo*)p->iobuf;
1486 PLONG(fi->nextfree, bp->freeptr);
1487 PLONG(fi->freeclust, bp->freeclusters);
1495 * Contiguous falloc; if we can, use lastclust+1.
1496 * Otherwise, move the file to get some space.
1497 * If there just isn't enough contiguous space
1498 * anywhere on disk, fail.
1505 if((l=makecontig(f, 8)) >= 0)
1507 return makecontig(f, 1);
1511 * Check whether a file is contiguous.
1514 iscontig(Xfs *xf, Dosdir *d)
1518 clust = getstart(xf, d);
1523 next = getfat(xf, clust);
1533 * Make a file contiguous, with nextra clusters of
1534 * free space after it for later expansion.
1535 * Return the number of the first new cluster.
1538 makecontig(Xfile *f, int nextra)
1545 long clust, next, last, start, rclust, wclust, eclust, ostart;
1546 int isok, i, n, nclust, nrun, rs, ws;
1555 clust = fileclust(f, 0, 0);
1556 chat("clust %#lux", clust);
1561 next = getfat(xf, clust);
1569 chat("nclust %d\n", nclust);
1571 if(isok && clust != -1) {
1572 eclust = clust+1; /* eclust = first cluster past file */
1573 assert(eclust == fileclust(f, 0, 0)+nclust);
1574 for(i=0; i<nextra; i++)
1575 if(getfat(xf, eclust+i) != 0)
1577 if(i == nextra) { /* they were all free */
1578 chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1));
1579 assert(getfat(xf, eclust-1) == 0xffffffff);
1580 putfat(xf, eclust-1, eclust);
1581 putfat(xf, eclust, 0xffffffff);
1582 bp->freeptr = clust+1; /* to help keep the blocks free */
1587 /* need to search for nclust+nextra contiguous free blocks */
1592 if(getfat(xf, n) == 0) {
1597 if(nrun >= nclust+nextra)
1601 if(++n >= bp->fatclusters)
1603 if(n == bp->freeptr) {
1610 /* copy old data over */
1614 for(i=0; i<nclust+nextra; i++)
1615 assert(getfat(xf, start+i) == 0);
1617 chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1620 for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){
1621 rs = clust2sect(bp, rclust);
1622 ws = clust2sect(bp, wclust);
1623 for(i=0; i<bp->clustsize; i++, rs++, ws++){
1624 rp = getsect(xf, rs);
1627 wp = getosect(xf, ws);
1629 memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1634 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1635 next = getfat(xf, rclust);
1636 putfat(xf, wclust, wclust+1);
1640 /* now wclust points at the first new cluster; chain it in */
1641 chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust);
1642 assert(wclust == start+nclust);
1643 putfat(xf, wclust, 0xffffffff); /* end of file */
1645 /* update directory entry to point at new start */
1646 ostart = fileclust(f, 0, 0);
1647 putstart(xf, d, start);
1649 /* check our work */
1651 clust = fileclust(f, 0, 0);
1655 next = getfat(xf, clust);
1658 assert(next == clust+1);
1662 chat("chain check: len %d\n", i);
1663 assert(i == nclust+1);
1665 /* succeeded; remove old chain. */
1666 for(rclust = ostart; rclust > 0; rclust = next){
1667 next = getfat(xf, rclust);
1668 putfat(xf, rclust, 0); /* free cluster */
1671 return start+nclust;
1677 Dosbpb *bp = xf->ptr;
1683 if(getfat(xf, n) == 0)
1685 if(++n >= bp->fatclusters)
1687 if(n == bp->freeptr)
1691 if(bp->freeptr >= bp->fatclusters)
1692 bp->freeptr = FATRESRV;
1693 putfat(xf, n, 0xffffffff);
1694 k = clust2sect(bp, n);
1695 for(i=0; i<bp->clustsize; i++){
1696 p = getosect(xf, k+i);
1697 memset(p->iobuf, 0, bp->sectsize);
1705 ffree(Xfs *xf, long start)
1707 putfat(xf, start, 0);
1711 clust2sect(Dosbpb *bp, long clust)
1713 return bp->dataaddr + (clust - FATRESRV) * bp->clustsize;
1717 sect2clust(Dosbpb *bp, long sect)
1721 c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV;
1722 assert(sect == clust2sect(bp, c));
1727 puttime(Dosdir *d, long s)
1736 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1738 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1740 PSHORT(d->adate, x);
1749 i = GSHORT(dp->time);
1751 tm.min = (i >> 5) & 63;
1752 tm.sec = (i & 31) << 1;
1753 i = GSHORT(dp->date);
1754 tm.year = 80 + (i >> 9);
1755 tm.mon = ((i >> 5) & 15) - 1;
1765 * structure dumps for debugging
1768 bootdump(int fd, Dosboot *b)
1772 Binit(&bp, fd, OWRITE);
1773 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1774 b->magic[0], b->magic[1], b->magic[2]);
1775 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1776 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1777 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1778 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1779 Bprint(&bp, "nfats: %d\n", b->nfats);
1780 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1781 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1782 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1783 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1784 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1785 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1786 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1787 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1788 Bprint(&bp, "driveno: %d\n", b->driveno);
1789 Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0);
1790 Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig);
1791 Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid));
1792 Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label);
1797 bootdump32(int fd, Dosboot32 *b)
1801 Binit(&bp, fd, OWRITE);
1802 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1803 b->magic[0], b->magic[1], b->magic[2]);
1804 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1805 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1806 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1807 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1808 Bprint(&bp, "nfats: %d\n", b->nfats);
1809 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1810 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1811 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1812 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1813 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1814 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1815 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1816 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1817 Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32));
1818 Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
1819 Bprint(&bp, "version: %d\n", GSHORT(b->version1));
1820 Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart));
1821 Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec));
1822 Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot));
1823 Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n",
1824 b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3],
1825 b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7],
1826 b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]);
1831 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1835 int fisec, bsec, res;
1837 fprint(fd, "\nfat32\n");
1838 bootdump32(fd, b32);
1839 res = GSHORT(b32->nresrv);
1840 bsec = GSHORT(b32->backupboot);
1841 if(bsec != 0 && bsec != 0xffff){
1843 fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1845 p1 = getsect(xf, bsec);
1847 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1849 fprint(fd, "\nbackup boot\n");
1850 bootdump32(fd, (Dosboot32*)p1->iobuf);
1855 fisec = GSHORT(b32->infospec);
1856 if(fisec != 0 && fisec != 0xffff){
1858 fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res);
1860 p1 = getsect(xf, fisec);
1862 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1864 fprint(fd, "\nfat info %d\n", fisec);
1865 fi = (Fatinfo*)p1->iobuf;
1866 fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1);
1867 fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG);
1868 fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust));
1869 fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree));
1870 fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8));
1878 dirdump(void *vdbuf)
1880 static char attrchar[] = "rhsvda67";
1882 char *name, namebuf[DOSNAMELEN];
1883 char buf[128], *s, *ebuf;
1892 ebuf = buf + sizeof(buf);
1893 if((d->attr & 0xf) == 0xf){
1895 name = namebuf + DOSNAMELEN;
1897 name = getnamerunes(name, dbuf, 1);
1898 seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start));
1900 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1902 *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1904 i = GSHORT(d->time);
1905 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1906 i = GSHORT(d->date);
1907 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1909 i = GSHORT(d->ctime);
1910 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d+%d", i>>11, (i>>5)&63, (i&31)<<1, (int)d->ctimetenth);
1911 i = GSHORT(d->cdate);
1912 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1914 i = GSHORT(d->adate);
1915 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1917 seprint(s, ebuf, " %d %lud", GSHORT(d->start), (ulong)GLONG(d->length));
1923 cistrcmp(char *s1, char *s2)
1931 if(c1 >= 'A' && c1 <= 'Z')
1934 if(c2 >= 'A' && c2 <= 'Z')
1944 utftorunes(Rune *rr, char *s, int n)
1951 while(c = (uchar)*s){
1956 s += chartorune(r, s);