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",
148 bp->freeclusters, bp->freeptr);
156 bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
158 bp->dataaddr = bp->rootaddr + (bp->rootsize*DOSDIRSIZE + bp->sectsize-1)/bp->sectsize;
159 bp->freeptr = FATRESRV;
161 bp->fatclusters = FATRESRV + (bp->volsize - bp->dataaddr)/bp->clustsize;
165 else if(bp->fatclusters < 4087)
170 chat("fatbits=%d (%ld clusters)...", bp->fatbits, bp->fatclusters);
171 for(i=0; i<b->nfats; i++)
172 chat("fat %d: %lld...", i, bp->fataddr+i*bp->fatsize);
173 chat("root: %lld...", bp->rootaddr);
174 chat("data: %lld...", bp->dataaddr);
180 * initialize f to the root directory
181 * this file has no Dosdir entry,
182 * so we special case it all over.
190 memset(dp, 0, sizeof(Dosptr));
209 p = getsect(f->xf, dp->addr);
214 * we could also make up a Dosdir for the root
217 if(!isroot(dp->addr)){
218 if(f->qid.path != QIDPATH(dp)){
219 chat("qid mismatch f=%#llux d=%#llux...", f->qid.path, QIDPATH(dp));
224 dp->d = (Dosdir *)&p->iobuf[dp->offset];
244 getstart(Xfs *xf, Dosdir *d)
248 start = GSHORT(d->start);
250 start |= GSHORT(d->hstart)<<16;
255 putstart(Xfs *xf, Dosdir *d, long start)
257 PSHORT(d->start, start);
259 PSHORT(d->hstart, start>>16);
263 * return the disk cluster for the iclust cluster in f
266 fileclust(Xfile *f, long iclust, int cflag)
271 long start, clust, nskip, next;
279 * asking for the cluster of the root directory
280 * is not a well-formed question, since the root directory
281 * does not begin on a cluster boundary.
283 if(!f->xf->isfat32 && isroot(dp->addr))
286 if(f->xf->isfat32 && isroot(dp->addr)){
287 start = bp->rootstart;
289 start = getstart(f->xf, d);
294 start = falloc(f->xf);
299 putstart(f->xf, d, start);
300 dp->p->flags |= BMOD;
304 if(dp->clust == 0 || iclust < dp->iclust){
309 nskip = iclust - dp->iclust;
311 if(chatty > 1 && nskip > 0)
312 chat("clust %#lx, skip %ld...", clust, nskip);
318 next = getfat(f->xf, clust);
320 chat("->%#lx", next);
326 if(d && (d->attr&DSYSTEM)){
330 /* cfalloc will call putfat for us, since clust may change */
332 next = falloc(f->xf);
335 putfat(f->xf, clust, next);
346 chat(" clust(%#lx)=%#lx...", iclust, clust);
351 * return the disk sector for the isect disk sector in f
354 fileaddr(Xfile *f, long isect, int cflag)
362 if(!f->xf->isfat32 && isroot(dp->addr)){
363 if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
365 return bp->rootaddr + isect;
367 clust = fileclust(f, isect/bp->clustsize, cflag);
371 return clust2sect(bp, clust) + isect%bp->clustsize;
385 if(c == ':' && trspaces)
392 * classify the file name as one of
393 * Invalid - contains a bad character
394 * Short - short valid 8.3 name, no lowercase letters
395 * ShortLower - short valid 8.3 name except for lowercase letters
399 classifyname(char *buf)
402 int c, isextended, is8dot3, islower, ndot;
409 while(c = (uchar)*p){
410 if(c&0x80) /* UTF8 */
415 }else if(strchr("+,:;=[] ", c))
419 if('a' <= c && c <= 'z')
424 is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
426 if(!isextended && is8dot3){
435 * make an alias for a valid long file name
438 mkalias(char *name, char *sname, int id)
441 char *s, *e, sid[10];
444 e = strrchr(name, '.');
446 e = strchr(name, '\0');
450 while(s < e && i < 6){
454 s += chartorune(&r, s);
457 v = snprint(sid, 10, "%d", id);
461 strcpy(&sname[i], sid);
467 panic("bad mkalias");
468 while(*e && i < esuf){
472 e += chartorune(&r, e);
474 if(sname[i-1] == '.')
480 * check for valid plan 9 names,
484 /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1,
485 /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1,
486 /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1,
487 /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1,
488 /* [' '] 1, let's try this -rsc */
497 if(*elem == ' ' && trspaces)
499 if(isfrog[*(uchar*)elem])
507 * look for a directory entry matching name
508 * always searches for long names which match a short name
511 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype)
517 char buf[261], *bname;
518 int isect, o, o1, islong, have, need, sum;
519 vlong addr, addr1, addr2, prevaddr, prevaddr1;
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;
650 for(isect=0;; isect++){
651 addr = fileaddr(f, isect, 0);
654 p = getsect(xf, addr);
657 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
658 d = (Dosdir *)&p->iobuf[o];
659 if(d->name[0] == 0x00){
663 if(d->name[0] == DOSEMPTY)
665 if(d->name[0] == '.')
678 readdir(Xfile *f, void *vbuf, vlong offset, long count)
683 int isect, o, islong, sum;
688 char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN];
700 for(isect=0;; isect++){
701 addr = fileaddr(f, isect, 0);
704 p = getsect(xf, addr);
707 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
708 d = (Dosdir *)&p->iobuf[o];
709 if(d->name[0] == 0x00){
713 if(d->name[0] == DOSEMPTY)
716 if(d->name[0] == '.'){
717 if(d->name[1] == ' ' || d->name[1] == 0)
719 if(d->name[1] == '.' &&
720 (d->name[2] == ' ' || d->name[2] == 0))
723 if((d->attr & 0xf) == 0xf){
724 name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1);
727 if(d->attr & DVLABEL){
732 getdir(xf, &dir, d, addr, o);
733 if(islong == 1 && nameok(name) && sum == aliassum(d))
736 n = convD2M(&dir, &buf[rcnt], count - rcnt);
738 if(n <= BIT16SZ){ /* no room for next entry */
760 * set up ndp for a directory's parent
761 * the hardest part is setting up paddr
764 walkup(Xfile *f, Dosptr *ndp)
770 long o, so, start, pstart, ppstart, st, ppclust;
775 memset(ndp, 0, sizeof(Dosptr));
778 ndp->addr = dp->paddr;
779 ndp->offset = dp->poffset;
781 chat("walkup: paddr=%#llx...", dp->paddr);
784 * root's paddr is always itself
786 if(isroot(dp->paddr))
790 * find the start of our parent's directory
792 p = getsect(f->xf, dp->paddr);
795 xd = (Dosdir *)&p->iobuf[dp->poffset];
797 start = getstart(f->xf, xd);
798 chat("start=%#lx...", start);
804 * verify that parent's . points to itself
806 p = getsect(f->xf, clust2sect(bp, start));
809 xd = (Dosdir *)p->iobuf;
811 st = getstart(f->xf, xd);
812 if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st)
816 * parent's .. is the next entry, and has start of parent's parent
820 if(xd->name[0] != '.' || xd->name[1] != '.')
822 pstart = getstart(f->xf, xd);
826 * we're done if parent is root
828 if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart)
832 * verify that parent's . points to itself
834 p = getsect(f->xf, clust2sect(bp, pstart));
836 chat("getsect %ld failed\n", pstart);
839 xd = (Dosdir *)p->iobuf;
841 st = getstart(f->xf, xd);
842 if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st)
846 * parent's parent's .. is the next entry, and has start of parent's parent's parent
850 if(xd->name[0] != '.' || xd->name[1] != '.')
852 ppstart = getstart(f->xf, xd);
856 * open parent's parent's parent, and walk through it until parent's parent is found
857 * need this to find parent's parent's addr and offset
860 if(f->xf->isfat32 && ppclust == 0){
861 ppclust = bp->rootstart;
862 chat("ppclust 0, resetting to rootstart\n");
864 k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr;
865 p = getsect(f->xf, k);
867 chat("getsect %lld failed\n", k);
870 xd = (Dosdir *)p->iobuf;
873 st = getstart(f->xf, xd);
874 if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st)
878 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
879 xd = (Dosdir *)&p->iobuf[o];
880 if(xd->name[0] == 0x00){
884 if(xd->name[0] == DOSEMPTY)
886 st = getstart(f->xf, xd);
891 if(so%bp->clustsize == 0){
893 ppclust = getfat(f->xf, ppclust);
896 chat("getfat %ld failed\n", ppclust);
900 k = clust2sect(bp, ppclust) + so%bp->clustsize;
902 if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
904 k = bp->rootaddr + so;
907 p = getsect(f->xf, k);
909 chat("getsect %lld failed\n", k);
926 readfile(Xfile *f, void *vbuf, vlong offset, long count)
929 Dosbpb *bp = xf->ptr;
938 if(offset >= MAXFILELEN)
940 if(offset+count > MAXFILELEN)
941 count = MAXFILELEN - offset;
944 length = GLONG(d->length);
948 if(offset+count >= length)
949 count = length - offset;
950 isect = offset/bp->sectsize;
951 o = offset%bp->sectsize;
953 addr = fileaddr(f, isect++, 0);
956 c = bp->sectsize - o;
959 p = getsect(xf, addr);
962 memmove(&buf[rcnt], &p->iobuf[o], c);
972 writefile(Xfile *f, void *vbuf, vlong offset, long count)
975 Dosbpb *bp = xf->ptr;
982 ulong length, rcnt, dlen;
984 if(offset >= MAXFILELEN)
986 if(offset+count > MAXFILELEN)
987 count = MAXFILELEN - offset;
992 isect = offset/bp->sectsize;
993 o = offset%bp->sectsize;
995 addr = fileaddr(f, isect++, 1);
998 c = bp->sectsize - o;
1001 if(c == bp->sectsize){
1002 p = getosect(xf, addr);
1005 p = getsect(xf, addr);
1009 memmove(&p->iobuf[o], &buf[rcnt], c);
1019 dlen = GLONG(d->length);
1021 length = offset+rcnt;
1022 else if(dp->addr && dp->clust){
1023 c = bp->clustsize*bp->sectsize;
1024 if(dp->iclust > (dlen+c-1)/c)
1025 length = (ulong)c*dp->iclust;
1028 PLONG(d->length, length);
1030 dp->p->flags |= BMOD;
1035 truncfile(Xfile *f, vlong length)
1038 Dosbpb *bp = xf->ptr;
1039 Dosptr *dp = f->ptr;
1044 if(length > MAXFILELEN)
1048 clust = getstart(f->xf, d);
1051 putstart(f->xf, d, 0);
1055 next = getfat(xf, clust);
1057 putfat(xf, clust, 0);
1059 n -= bp->clustsize*bp->sectsize;
1063 PLONG(d->length, length);
1066 dp->p->flags |= BMOD;
1071 putdir(Dosdir *d, Dir *dp)
1078 if(dp->mode & DMEXCL)
1081 d->attr &= ~DSYSTEM;
1084 puttime(d, dp->mtime);
1088 * should extend this to deal with
1089 * creation and access dates
1092 getdir(Xfs *xfs, Dir *dp, Dosdir *d, vlong addr, int offset)
1094 if(d == nil || addr == 0)
1095 panic("getdir on root");
1098 getname(dp->name, d);
1100 dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1104 if(d->attr & DRONLY)
1108 dp->atime = gtime(d);
1109 dp->mtime = dp->atime;
1110 dp->qid.type = QTFILE;
1112 dp->qid.type = QTDIR;
1113 dp->mode |= DMDIR|0111;
1116 dp->length = GLONG(d->length);
1117 if(d->attr & DSYSTEM){
1119 if(iscontig(xfs, d))
1120 dp->mode |= DMAPPEND;
1129 getname(char *p, Dosdir *d)
1135 if(c == '\0' || c == ' ')
1137 if(i == 0 && c == 0x05)
1143 if(c == '\0' || c == ' ')
1153 getnamerunes(char *dst, uchar *buf, int step)
1157 char dbuf[DOSRUNE * UTFmax + 1], *d;
1161 for(i = 1; r && i < 11; i += 2){
1162 r = buf[i] | (buf[i+1] << 8);
1163 d += runetochar(d, &r);
1165 for(i = 14; r && i < 26; i += 2){
1166 r = buf[i] | (buf[i+1] << 8);
1167 d += runetochar(d, &r);
1169 for(i = 28; r && i < 32; i += 2){
1170 r = buf[i] | (buf[i+1] << 8);
1171 d += runetochar(d, &r);
1177 memmove(dst, dbuf, d - dbuf);
1188 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1191 * validation checks to make sure we're
1192 * making up a consistent name
1194 if(buf[11] != 0xf || buf[12] != 0){
1199 if((buf[0] & 0xc0) == 0x40){
1200 *islong = buf[0] & 0x3f;
1202 d = dbuf + DOSNAMELEN;
1204 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1211 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1212 *islong = buf[0] & 0x3f;
1227 return getnamerunes(d, buf, step);
1231 putname(char *p, Dosdir *d)
1235 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1236 for(i=0; i<sizeof d->name; i++){
1237 if(*p == 0 || *p == '.')
1239 d->name[i] = toupper(*p++);
1241 p = strrchr(p, '.');
1243 for(i=0; i<sizeof d->ext; i++){
1246 d->ext[i] = toupper(*p);
1252 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1258 memset(&ds, 0xff, sizeof ds);
1261 ds.reserved[1] = sum;
1265 ds.name[0] = 0x40 | curslot;
1267 ds.name[0] = curslot;
1268 memmove(slot, &ds, sizeof ds);
1270 j = (curslot-1) * DOSRUNE;
1272 for(i = 1; i < 11; i += 2){
1279 for(i = 14; i < 26; i += 2){
1286 for(i = 28; i < 32; i += 2){
1303 for(i = 0; i < 11; i++)
1304 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1309 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1313 Rune longname[DOSNAMELEN+1];
1314 int i, first, sum, nds, len;
1316 /* calculate checksum */
1317 putname(sname, &tmpd);
1318 sum = aliassum(&tmpd);
1322 len = utftorunes(longname, name, DOSNAMELEN);
1324 chat("utftorunes %s =", name);
1325 for(i=0; i<len; i++)
1326 chat(" %.4X", longname[i]);
1329 for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1330 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1333 if(ndp->offset == bp->sectsize){
1334 chat("long name moving over sector boundary\n");
1335 ndp->p->flags |= BMOD;
1340 * switch to the next cluster for a long entry
1341 * naddr should be set up correctly by searchdir
1343 ndp->prevaddr = ndp->addr;
1344 ndp->addr = ndp->naddr;
1348 ndp->p = getsect(xf, ndp->addr);
1352 ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1359 getfat(Xfs *xf, long n)
1361 Dosbpb *bp = xf->ptr;
1367 if(n < FATRESRV || n >= bp->fatclusters)
1371 if(k >= bp->fatsize*bp->sectsize)
1373 sect = k/bp->sectsize + bp->fataddr;
1375 p = getsect(xf, sect);
1379 if(o >= bp->sectsize){
1381 p = getsect(xf, sect+1);
1386 k |= p->iobuf[o++]<<8;
1388 /* fat32 is really fat28 */
1389 k |= p->iobuf[o++] << 16;
1390 k |= (p->iobuf[o] & 0x0f) << 24;
1401 chat("fat(%#lx)=%#lux...", n, k);
1404 * This is a very strange check for out of range.
1405 * As a concrete example, for a 16-bit FAT,
1406 * FFF8 through FFFF all signify ``end of cluster chain.''
1407 * This generalizes to other-sized FATs.
1409 if(k >= (1UL << fb) - 8)
1416 putfat(Xfs *xf, int n, ulong val)
1426 if(n < FATRESRV || n >= bp->fatclusters)
1427 panic("putfat n=%d", n);
1428 k = (bp->fatbits * n) >> 3;
1429 if(k >= bp->fatsize*bp->sectsize)
1431 sect = k/bp->sectsize + bp->fataddr;
1432 esect = sect + bp->nfats * bp->fatsize;
1433 for(; sect<esect; sect+=bp->fatsize){
1435 p = getsect(xf, sect);
1438 switch(bp->fatbits){
1441 p->iobuf[o] &= 0x0f;
1442 p->iobuf[o++] |= val<<4;
1443 if(o >= bp->sectsize){
1446 p = getsect(xf, sect+1);
1451 p->iobuf[o] = val>>4;
1453 p->iobuf[o++] = val;
1454 if(o >= bp->sectsize){
1457 p = getsect(xf, sect+1);
1462 p->iobuf[o] &= 0xf0;
1463 p->iobuf[o] |= (val>>8) & 0x0f;
1467 p->iobuf[o++] = val;
1468 p->iobuf[o] = val>>8;
1470 case 32: /* fat32 is really fat28 */
1471 p->iobuf[o++] = val;
1472 p->iobuf[o++] = val>>8;
1473 p->iobuf[o++] = val>>16;
1474 p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f);
1477 panic("putfat fatbits");
1489 p = getsect(xf, bp->fatinfo);
1491 fi = (Fatinfo*)p->iobuf;
1492 PLONG(fi->nextfree, bp->freeptr);
1493 PLONG(fi->freeclust, bp->freeclusters);
1501 * Contiguous falloc; if we can, use lastclust+1.
1502 * Otherwise, move the file to get some space.
1503 * If there just isn't enough contiguous space
1504 * anywhere on disk, fail.
1511 if((l=makecontig(f, 8)) >= 0)
1513 return makecontig(f, 1);
1517 * Check whether a file is contiguous.
1520 iscontig(Xfs *xf, Dosdir *d)
1524 clust = getstart(xf, d);
1529 next = getfat(xf, clust);
1539 * Make a file contiguous, with nextra clusters of
1540 * free space after it for later expansion.
1541 * Return the number of the first new cluster.
1544 makecontig(Xfile *f, int nextra)
1551 long clust, next, last, start, rclust, wclust, eclust, ostart;
1552 int isok, i, n, nclust, nrun;
1562 clust = fileclust(f, 0, 0);
1563 chat("clust %#lux", clust);
1568 next = getfat(xf, clust);
1576 chat("nclust %d\n", nclust);
1578 if(isok && clust != -1) {
1579 eclust = clust+1; /* eclust = first cluster past file */
1580 assert(eclust == fileclust(f, 0, 0)+nclust);
1581 for(i=0; i<nextra; i++)
1582 if(getfat(xf, eclust+i) != 0)
1584 if(i == nextra) { /* they were all free */
1585 chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1));
1586 assert(getfat(xf, eclust-1) == 0xffffffff);
1587 putfat(xf, eclust-1, eclust);
1588 putfat(xf, eclust, 0xffffffff);
1589 bp->freeptr = clust+1; /* to help keep the blocks free */
1594 /* need to search for nclust+nextra contiguous free blocks */
1599 if(getfat(xf, n) == 0) {
1604 if(nrun >= nclust+nextra)
1608 if(++n >= bp->fatclusters)
1610 if(n == bp->freeptr) {
1617 /* copy old data over */
1621 for(i=0; i<nclust+nextra; i++)
1622 assert(getfat(xf, start+i) == 0);
1624 chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1627 for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){
1628 rs = clust2sect(bp, rclust);
1629 ws = clust2sect(bp, wclust);
1630 for(i=0; i<bp->clustsize; i++, rs++, ws++){
1631 rp = getsect(xf, rs);
1634 wp = getosect(xf, ws);
1636 memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1641 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1642 next = getfat(xf, rclust);
1643 putfat(xf, wclust, wclust+1);
1647 /* now wclust points at the first new cluster; chain it in */
1648 chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust);
1649 assert(wclust == start+nclust);
1650 putfat(xf, wclust, 0xffffffff); /* end of file */
1652 /* update directory entry to point at new start */
1653 ostart = fileclust(f, 0, 0);
1654 putstart(xf, d, start);
1656 /* check our work */
1658 clust = fileclust(f, 0, 0);
1662 next = getfat(xf, clust);
1665 assert(next == clust+1);
1669 chat("chain check: len %d\n", i);
1670 assert(i == nclust+1);
1672 /* succeeded; remove old chain. */
1673 for(rclust = ostart; rclust > 0; rclust = next){
1674 next = getfat(xf, rclust);
1675 putfat(xf, rclust, 0); /* free cluster */
1678 return start+nclust;
1684 Dosbpb *bp = xf->ptr;
1690 if(getfat(xf, n) == 0)
1692 if(++n >= bp->fatclusters)
1694 if(n == bp->freeptr)
1698 if(bp->freeptr >= bp->fatclusters)
1699 bp->freeptr = FATRESRV;
1700 putfat(xf, n, 0xffffffff);
1701 k = clust2sect(bp, n);
1702 for(i=0; i<bp->clustsize; i++){
1703 p = getosect(xf, k+i);
1704 memset(p->iobuf, 0, bp->sectsize);
1712 ffree(Xfs *xf, long start)
1714 putfat(xf, start, 0);
1718 clust2sect(Dosbpb *bp, long clust)
1720 return bp->dataaddr + ((vlong)(clust - FATRESRV) * bp->clustsize);
1724 sect2clust(Dosbpb *bp, vlong sect)
1726 return ((sect - bp->dataaddr) / bp->clustsize) + FATRESRV;
1730 puttime(Dosdir *d, long s)
1739 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1741 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1743 PSHORT(d->adate, x);
1752 i = GSHORT(dp->time);
1754 memset(&tm, 0, sizeof(tm));
1756 tm.min = (i >> 5) & 63;
1757 tm.sec = (i & 31) << 1;
1758 i = GSHORT(dp->date);
1759 tm.year = 80 + (i >> 9);
1760 tm.mon = ((i >> 5) & 15) - 1;
1770 * structure dumps for debugging
1773 bootdump(int fd, Dosboot *b)
1777 Binit(&bp, fd, OWRITE);
1778 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1779 b->magic[0], b->magic[1], b->magic[2]);
1780 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1781 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1782 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1783 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1784 Bprint(&bp, "nfats: %d\n", b->nfats);
1785 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1786 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1787 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1788 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1789 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1790 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1791 Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden));
1792 Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize));
1793 Bprint(&bp, "driveno: %d\n", b->driveno);
1794 Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0);
1795 Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig);
1796 Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid));
1797 Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label);
1802 bootdump32(int fd, Dosboot32 *b)
1806 Binit(&bp, fd, OWRITE);
1807 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1808 b->magic[0], b->magic[1], b->magic[2]);
1809 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1810 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1811 Bprint(&bp, "clustsize: %d\n", b->clustsize);
1812 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1813 Bprint(&bp, "nfats: %d\n", b->nfats);
1814 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1815 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1816 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1817 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1818 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1819 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1820 Bprint(&bp, "nhidden: %lud\n", GLONG(b->nhidden));
1821 Bprint(&bp, "bigvolsize: %lud\n", GLONG(b->bigvolsize));
1822 Bprint(&bp, "fatsize32: %lud\n", GLONG(b->fatsize32));
1823 Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
1824 Bprint(&bp, "version: %d\n", GSHORT(b->version1));
1825 Bprint(&bp, "rootstart: %lud\n", GLONG(b->rootstart));
1826 Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec));
1827 Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot));
1828 Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n",
1829 b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3],
1830 b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7],
1831 b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]);
1836 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1840 int fisec, bsec, res;
1842 fprint(fd, "\nfat32\n");
1843 bootdump32(fd, b32);
1844 res = GSHORT(b32->nresrv);
1845 bsec = GSHORT(b32->backupboot);
1846 if(bsec != 0 && bsec != 0xffff){
1848 fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1850 p1 = getsect(xf, bsec);
1852 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1854 fprint(fd, "\nbackup boot\n");
1855 bootdump32(fd, (Dosboot32*)p1->iobuf);
1860 fisec = GSHORT(b32->infospec);
1861 if(fisec != 0 && fisec != 0xffff){
1863 fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res);
1865 p1 = getsect(xf, fisec);
1867 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1869 fprint(fd, "\nfat info %d\n", fisec);
1870 fi = (Fatinfo*)p1->iobuf;
1871 fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1);
1872 fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG);
1873 fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust));
1874 fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree));
1875 fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8));
1883 dirdump(void *vdbuf)
1885 static char attrchar[] = "rhsvda67";
1887 char *name, namebuf[DOSNAMELEN];
1888 char buf[128], *s, *ebuf;
1897 ebuf = buf + sizeof(buf);
1898 if((d->attr & 0xf) == 0xf){
1900 name = namebuf + DOSNAMELEN;
1902 name = getnamerunes(name, dbuf, 1);
1903 seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start));
1905 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1907 *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1909 i = GSHORT(d->time);
1910 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1911 i = GSHORT(d->date);
1912 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1914 i = GSHORT(d->ctime);
1915 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d+%d", i>>11, (i>>5)&63, (i&31)<<1, (int)d->ctimetenth);
1916 i = GSHORT(d->cdate);
1917 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1919 i = GSHORT(d->adate);
1920 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1922 seprint(s, ebuf, " %d %lud", GSHORT(d->start), GLONG(d->length));
1928 cistrcmp(char *s1, char *s2)
1936 if(c1 >= 'A' && c1 <= 'Z')
1939 if(c2 >= 'A' && c2 <= 'Z')
1949 utftorunes(Rune *rr, char *s, int n)
1956 while(c = (uchar)*s){
1961 s += chartorune(r, s);