1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include "iotrack.h"
7 #include "dat.h"
8 #include "fns.h"
9
10 static uchar isdos[256];
11
12 int
isdosfs(uchar * buf)13 isdosfs(uchar *buf)
14 {
15 /*
16 * When dynamic disc managers move the disc partition,
17 * they make it start with 0xE9.
18 */
19 if(buf[0] == 0xE9)
20 return 1;
21
22 /*
23 * Check if the jump displacement (magic[1]) is too short for a FAT.
24 *
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,
29 * page 11.
30 */
31 if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */)
32 return 1;
33 if(chatty)
34 fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]);
35
36 return 0;
37 }
38
39 int
dosfs(Xfs * xf)40 dosfs(Xfs *xf)
41 {
42 Iosect *p, *p1;
43 Dosboot *b;
44 Fatinfo *fi;
45 Dosboot32 *b32;
46 Dosbpb *bp;
47 long fisec, extflags;
48 int i;
49
50 if(!isdos['a']){
51 for(i = 'a'; i <= 'z'; i++)
52 isdos[i] = 1;
53 for(i = 'A'; i <= 'Z'; i++)
54 isdos[i] = 1;
55 for(i = '0'; i <= '9'; i++)
56 isdos[i] = 1;
57 isdos['$'] = 1;
58 isdos['%'] = 1;
59 isdos['''] = 1;
60 isdos['-'] = 1;
61 isdos['_'] = 1;
62 isdos['@'] = 1;
63 isdos['~'] = 1;
64 isdos['`'] = 1;
65 isdos['!'] = 1;
66 isdos['('] = 1;
67 isdos[')'] = 1;
68 isdos['{'] = 1;
69 isdos['}'] = 1;
70 isdos['^'] = 1;
71 isdos['#'] = 1;
72 isdos['&'] = 1;
73 }
74
75 p = getsect(xf, 0);
76 if(p == 0)
77 return -1;
78
79 b = (Dosboot*)p->iobuf;
80 if(b->clustsize == 0 || isdosfs(p->iobuf) == 0){
81 putsect(p);
82 return -1;
83 }
84
85 bp = malloc(sizeof(Dosbpb));
86 memset(bp, 0, sizeof(Dosbpb)); /* clear lock */
87 xf->ptr = bp;
88 xf->fmt = 1;
89
90 bp->sectsize = GSHORT(b->sectsize);
91 bp->clustsize = b->clustsize;
92 bp->nresrv = GSHORT(b->nresrv);
93 bp->nfats = b->nfats;
94 bp->rootsize = GSHORT(b->rootsize);
95 bp->volsize = GSHORT(b->volsize);
96 if(bp->volsize == 0)
97 bp->volsize = GLONG(b->bigvolsize);
98 bp->mediadesc = b->mediadesc;
99 bp->fatsize = GSHORT(b->fatsize);
100 bp->fataddr = GSHORT(b->nresrv);
101
102 bp->fatinfo = 0;
103
104 if(bp->fatsize == 0){ /* is FAT32 */
105 if(chatty)
106 bootsecdump32(2, xf, (Dosboot32*)b);
107 xf->isfat32 = 1;
108 b32 = (Dosboot32*)b;
109 bp->fatsize = GLONG(b32->fatsize32);
110 if(bp->fatsize == 0){
111 putsect(p);
112 if(chatty)
113 fprint(2, "fatsize 0\n");
114 return -1;
115 }
116 bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize;
117 bp->rootaddr = 0;
118 bp->rootstart = GLONG(b32->rootstart);
119
120 /*
121 * disable fat mirroring?
122 */
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;
128 bp->nfats = 1;
129 break;
130 }
131 }
132 }
133
134 /*
135 * fat free list info
136 */
137 bp->freeptr = FATRESRV;
138 fisec = GSHORT(b32->infospec);
139 if(fisec != 0 && fisec < GSHORT(b32->nresrv)){
140 p1 = getsect(xf, fisec);
141 if(p1 != nil){
142 fi = (Fatinfo*)p1->iobuf;
143 if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){
144 bp->fatinfo = fisec;
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);
148 }
149 putsect(p1);
150 }
151 }
152 }else{
153 if(chatty)
154 bootdump(2, b);
155 bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
156 bp->rootstart = 0;
157 i = bp->rootsize*DOSDIRSIZE + bp->sectsize-1;
158 i /= bp->sectsize;
159 bp->dataaddr = bp->rootaddr + i;
160 bp->freeptr = FATRESRV;
161 }
162 bp->fatclusters = FATRESRV+(bp->volsize - bp->dataaddr)/bp->clustsize;
163
164 if(xf->isfat32)
165 bp->fatbits = 32;
166 else if(bp->fatclusters < 4087)
167 bp->fatbits = 12;
168 else
169 bp->fatbits = 16;
170
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);
176 putsect(p);
177 return 0;
178 }
179
180 /*
181 * initialize f to the root directory
182 * this file has no Dosdir entry,
183 * so we special case it all over.
184 */
185 void
186 rootfile(Xfile *f)
187 {
188 Dosptr *dp;
189
190 dp = f->ptr;
191 memset(dp, 0, sizeof(Dosptr));
192 dp->prevaddr = -1;
193 }
194
195 int
196 isroot(ulong addr)
197 {
198 return addr == 0;
199 }
200
201 int
202 getfile(Xfile *f)
203 {
204 Dosptr *dp;
205 Iosect *p;
206
207 dp = f->ptr;
208 if(dp->p)
209 panic("getfile");
210 p = getsect(f->xf, dp->addr);
211 if(p == nil)
212 return -1;
213
214 /*
215 * we could also make up a Dosdir for the root
216 */
217 dp->d = nil;
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));
221 putsect(p);
222 errno = Enonexist;
223 return -1;
224 }
225 dp->d = (Dosdir *)&p->iobuf[dp->offset];
226 }
227 dp->p = p;
228 return 0;
229 }
230
231 void
232 putfile(Xfile *f)
233 {
234 Dosptr *dp;
235
236 dp = f->ptr;
237 if(!dp->p)
238 panic("putfile");
239 putsect(dp->p);
240 dp->p = nil;
241 dp->d = nil;
242 }
243
244 long
245 getstart(Xfs *xf, Dosdir *d)
246 {
247 long start;
248
249 start = GSHORT(d->start);
250 if(xf->isfat32)
251 start |= GSHORT(d->hstart)<<16;
252 return start;
253 }
254
255 void
256 putstart(Xfs *xf, Dosdir *d, long start)
257 {
258 PSHORT(d->start, start);
259 if(xf->isfat32)
260 PSHORT(d->hstart, start>>16);
261 }
262
263 /*
264 * return the disk cluster for the iclust cluster in f
265 */
266 long
267 fileclust(Xfile *f, long iclust, int cflag)
268 {
269 Dosbpb *bp;
270 Dosptr *dp;
271 Dosdir *d;
272 long start, clust, nskip, next;
273
274 bp = f->xf->ptr;
275 dp = f->ptr;
276 d = dp->d;
277 next = 0;
278
279 /*
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.
283 */
284 if(!f->xf->isfat32 && isroot(dp->addr))
285 return -1;
286
287 if(f->xf->isfat32 && isroot(dp->addr)){
288 start = bp->rootstart;
289 }else{
290 start = getstart(f->xf, d);
291 if(start == 0){
292 if(!cflag)
293 return -1;
294 mlock(bp);
295 start = falloc(f->xf);
296 unmlock(bp);
297 if(start <= 0)
298 return -1;
299 puttime(d, 0);
300 putstart(f->xf, d, start);
301 dp->p->flags |= BMOD;
302 dp->clust = 0;
303 }
304 }
305 if(dp->clust == 0 || iclust < dp->iclust){
306 clust = start;
307 nskip = iclust;
308 }else{
309 clust = dp->clust;
310 nskip = iclust - dp->iclust;
311 }
312 if(chatty > 1 && nskip > 0)
313 chat("clust %#lx, skip %ld...", clust, nskip);
314 if(clust <= 0)
315 return -1;
316 if(nskip > 0){
317 mlock(bp);
318 while(--nskip >= 0){
319 next = getfat(f->xf, clust);
320 if(chatty > 1)
321 chat("->%#lx", next);
322 if(next > 0){
323 clust = next;
324 continue;
325 }else if(!cflag)
326 break;
327 if(d && (d->attr&DSYSTEM)){
328 next = cfalloc(f);
329 if(next < 0)
330 break;
331 /* cfalloc will call putfat for us, since clust may change */
332 } else {
333 next = falloc(f->xf);
334 if(next < 0)
335 break;
336 putfat(f->xf, clust, next);
337 }
338 clust = next;
339 }
340 unmlock(bp);
341 if(next <= 0)
342 return -1;
343 dp->clust = clust;
344 dp->iclust = iclust;
345 }
346 if(chatty > 1)
347 chat(" clust(%#lx)=%#lx...", iclust, clust);
348 return clust;
349 }
350
351 /*
352 * return the disk sector for the isect disk sector in f
353 */
354 long
355 fileaddr(Xfile *f, long isect, int cflag)
356 {
357 Dosbpb *bp;
358 Dosptr *dp;
359 long clust;
360
361 bp = f->xf->ptr;
362 dp = f->ptr;
363 if(!f->xf->isfat32 && isroot(dp->addr)){
364 if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
365 return -1;
366 return bp->rootaddr + isect;
367 }
368 clust = fileclust(f, isect/bp->clustsize, cflag);
369 if(clust < 0)
370 return -1;
371
372 return clust2sect(bp, clust) + isect%bp->clustsize;
373 }
374
375 /*
376 * translate names
377 */
378 void
379 fixname(char *buf)
380 {
381 int c;
382 char *p;
383
384 p = buf;
385 while(c = *p){
386 if(c == ':' && trspaces)
387 *p = ' ';
388 p++;
389 }
390 }
391
392 /*
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
397 * Long - long name
398 */
399 int
400 classifyname(char *buf)
401 {
402 char *p, *dot;
403 int c, isextended, is8dot3, islower, ndot;
404
405 p = buf;
406 isextended = 0;
407 islower = 0;
408 dot = nil;
409 ndot = 0;
410 while(c = (uchar)*p){
411 if(c&0x80) /* UTF8 */
412 isextended = 1;
413 else if(c == '.'){
414 dot = p;
415 ndot++;
416 }else if(strchr("+,:;=[] ", c))
417 isextended = 1;
418 else if(!isdos[c])
419 return Invalid;
420 if('a' <= c && c <= 'z')
421 islower = 1;
422 p++;
423 }
424
425 is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
426
427 if(!isextended && is8dot3){
428 if(islower)
429 return ShortLower;
430 return Short;
431 }
432 return Long;
433 }
434
435 /*
436 * make an alias for a valid long file name
437 */
438 void
439 mkalias(char *name, char *sname, int id)
440 {
441 Rune r;
442 char *s, *e, sid[10];
443 int i, esuf, v;
444
445 e = strrchr(name, '.');
446 if(e == nil)
447 e = strchr(name, '\0');
448
449 s = name;
450 i = 0;
451 while(s < e && i < 6){
452 if(isdos[(uchar)*s])
453 sname[i++] = *s++;
454 else
455 s += chartorune(&r, s);
456 }
457
458 v = snprint(sid, 10, "%d", id);
459 if(i + 1 + v > 8)
460 i = 8 - 1 - v;
461 sname[i++] = '~';
462 strcpy(&sname[i], sid);
463 i += v;
464
465 sname[i++] = '.';
466 esuf = i + 3;
467 if(esuf > 12)
468 panic("bad mkalias");
469 while(*e && i < esuf){
470 if(isdos[(uchar)*e])
471 sname[i++] = *e++;
472 else
473 e += chartorune(&r, e);
474 }
475 if(sname[i-1] == '.')
476 i--;
477 sname[i] = '\0';
478 }
479
480 /*
481 * check for valid plan 9 names,
482 * rewrite ' ' to ':'
483 */
484 char isfrog[256]={
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 */
490 ['/'] 1,
491 [0x7f] 1,
492 };
493
494 int
495 nameok(char *elem)
496 {
497 while(*elem) {
498 if(*elem == ' ' && trspaces)
499 *elem = ':';
500 if(isfrog[*(uchar*)elem])
501 return 0;
502 elem++;
503 }
504 return 1;
505 }
506
507 /*
508 * look for a directory entry matching name
509 * always searches for long names which match a short name
510 */
511 int
512 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype)
513 {
514 Xfs *xf;
515 Iosect *p;
516 Dosbpb *bp;
517 Dosdir *d;
518 char buf[261], *bname;
519 int isect, addr, o, addr1, addr2, prevaddr, prevaddr1, o1, islong, have, need, sum;
520
521 xf = f->xf;
522 bp = xf->ptr;
523 addr1 = -1;
524 addr2 = -1;
525 prevaddr1 = -1;
526 o1 = 0;
527 islong = 0;
528 sum = -1;
529
530 need = 1;
531 if(longtype!=Short && cflag)
532 need += (utflen(name) + DOSRUNE-1) / DOSRUNE;
533
534 memset(dp, 0, sizeof(Dosptr));
535 dp->prevaddr = -1;
536 dp->naddr = -1;
537 dp->paddr = ((Dosptr *)f->ptr)->addr;
538 dp->poffset = ((Dosptr *)f->ptr)->offset;
539
540 have = 0;
541 addr = -1;
542 bname = nil;
543 for(isect=0;; isect++){
544 prevaddr = addr;
545 addr = fileaddr(f, isect, cflag);
546 if(addr < 0)
547 break;
548 p = getsect(xf, addr);
549 if(p == 0)
550 break;
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)...");
555 putsect(p);
556 if(!cflag)
557 return -1;
558
559 /*
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
563 *
564 * have tells us the number of contiguous free dirs
565 * starting at addr1.o1; need are necessary to hold the long name.
566 */
567 if(addr1 < 0){
568 addr1 = addr;
569 prevaddr1 = prevaddr;
570 o1 = o;
571 }
572 if(addr2 < 0 && (bp->sectsize-o)/DOSDIRSIZE + have < need){
573 addr2 = fileaddr(f, isect+1, cflag);
574 if(addr2 < 0)
575 goto breakout;
576 }else if(addr2 < 0)
577 addr2 = addr;
578 if(addr2 == addr1)
579 addr2 = -1;
580 dp->addr = addr1;
581 dp->offset = o1;
582 dp->prevaddr = prevaddr1;
583 dp->naddr = addr2;
584 return 0;
585 }
586 if(d->name[0] == DOSEMPTY){
587 if(chatty)
588 fprint(2, "empty dir\n");
589
590 have++;
591 if(addr1 == -1){
592 addr1 = addr;
593 o1 = o;
594 prevaddr1 = prevaddr;
595 }
596 if(addr2 == -1 && have >= need)
597 addr2 = addr;
598 continue;
599 }
600 have = 0;
601 if(addr2 == -1)
602 addr1 = -1;
603
604 dirdump(d);
605 if((d->attr & 0xf) == 0xf){
606 bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1);
607 continue;
608 }
609 if(d->attr & DVLABEL){
610 islong = 0;
611 continue;
612 }
613 if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){
614 bname = buf;
615 getname(buf, d);
616 }
617 islong = 0;
618 if(cistrcmp(bname, name) != 0)
619 continue;
620 if(chatty)
621 fprint(2, "found\n");
622 if(cflag){
623 putsect(p);
624 return -1;
625 }
626 dp->addr = addr;
627 dp->prevaddr = prevaddr;
628 dp->offset = o;
629 dp->p = p;
630 dp->d = d;
631 return 0;
632 }
633 putsect(p);
634 }
635 breakout:
636 chat("end dir(1)...");
637 return -1;
638 }
639
640 int
641 emptydir(Xfile *f)
642 {
643 Xfs *xf = f->xf;
644 Dosbpb *bp = xf->ptr;
645 int isect, addr, o;
646 Iosect *p;
647 Dosdir *d;
648
649 for(isect=0;; isect++){
650 addr = fileaddr(f, isect, 0);
651 if(addr < 0)
652 break;
653 p = getsect(xf, addr);
654 if(p == 0)
655 return -1;
656 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
657 d = (Dosdir *)&p->iobuf[o];
658 if(d->name[0] == 0x00){
659 putsect(p);
660 return 0;
661 }
662 if(d->name[0] == DOSEMPTY)
663 continue;
664 if(d->name[0] == '.')
665 continue;
666 if(d->attr&DVLABEL)
667 continue;
668 putsect(p);
669 return -1;
670 }
671 putsect(p);
672 }
673 return 0;
674 }
675
676 long
677 readdir(Xfile *f, void *vbuf, long offset, long count)
678 {
679 Xfs *xf;
680 Dosbpb *bp;
681 Dir dir;
682 int isect, addr, o, islong, sum;
683 Iosect *p;
684 Dosdir *d;
685 long rcnt, n;
686 char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN];
687 uchar *buf;
688
689 buf = vbuf;
690 rcnt = 0;
691 xf = f->xf;
692 bp = xf->ptr;
693 if(count <= 0)
694 return 0;
695 islong = 0;
696 sum = -1;
697 name = nil;
698 for(isect=0;; isect++){
699 addr = fileaddr(f, isect, 0);
700 if(addr < 0)
701 break;
702 p = getsect(xf, addr);
703 if(p == 0)
704 return -1;
705 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
706 d = (Dosdir *)&p->iobuf[o];
707 if(d->name[0] == 0x00){
708 putsect(p);
709 return rcnt;
710 }
711 if(d->name[0] == DOSEMPTY)
712 continue;
713 dirdump(d);
714 if(d->name[0] == '.'){
715 if(d->name[1] == ' ' || d->name[1] == 0)
716 continue;
717 if(d->name[1] == '.' &&
718 (d->name[2] == ' ' || d->name[2] == 0))
719 continue;
720 }
721 if((d->attr & 0xf) == 0xf){
722 name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1);
723 continue;
724 }
725 if(d->attr & DVLABEL){
726 islong = 0;
727 continue;
728 }
729 dir.name = snamebuf;
730 getdir(xf, &dir, d, addr, o);
731 if(islong == 1 && nameok(name) && sum == aliassum(d))
732 dir.name = name;
733 islong = 0;
734 n = convD2M(&dir, &buf[rcnt], count - rcnt);
735 name = nil;
736 if(n <= BIT16SZ){ /* no room for next entry */
737 putsect(p);
738 return rcnt;
739 }
740 rcnt += n;
741 if(offset > 0){
742 offset -= rcnt;
743 rcnt = 0;
744 islong = 0;
745 continue;
746 }
747 if(rcnt == count){
748 putsect(p);
749 return rcnt;
750 }
751 }
752 putsect(p);
753 }
754 return rcnt;
755 }
756
757 /*
758 * set up ndp for a directory's parent
759 * the hardest part is setting up paddr
760 */
761 int
762 walkup(Xfile *f, Dosptr *ndp)
763 {
764 Dosbpb *bp;
765 Dosptr *dp;
766 Dosdir *xd;
767 Iosect *p;
768 long k, o, so, start, pstart, ppstart, st, ppclust;
769
770 bp = f->xf->ptr;
771 dp = f->ptr;
772 memset(ndp, 0, sizeof(Dosptr));
773 ndp->prevaddr = -1;
774 ndp->naddr = -1;
775 ndp->addr = dp->paddr;
776 ndp->offset = dp->poffset;
777
778 chat("walkup: paddr=%#lx...", dp->paddr);
779
780 /*
781 * root's paddr is always itself
782 */
783 if(isroot(dp->paddr))
784 return 0;
785
786 /*
787 * find the start of our parent's directory
788 */
789 p = getsect(f->xf, dp->paddr);
790 if(p == nil)
791 goto error;
792 xd = (Dosdir *)&p->iobuf[dp->poffset];
793 dirdump(xd);
794 start = getstart(f->xf, xd);
795 chat("start=%#lx...", start);
796 if(start == 0)
797 goto error;
798 putsect(p);
799
800 /*
801 * verify that parent's . points to itself
802 */
803 p = getsect(f->xf, clust2sect(bp, start));
804 if(p == nil)
805 goto error;
806 xd = (Dosdir *)p->iobuf;
807 dirdump(xd);
808 st = getstart(f->xf, xd);
809 if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st)
810 goto error;
811
812 /*
813 * parent's .. is the next entry, and has start of parent's parent
814 */
815 xd++;
816 dirdump(xd);
817 if(xd->name[0] != '.' || xd->name[1] != '.')
818 goto error;
819 pstart = getstart(f->xf, xd);
820 putsect(p);
821
822 /*
823 * we're done if parent is root
824 */
825 if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart)
826 return 0;
827
828 /*
829 * verify that parent's . points to itself
830 */
831 p = getsect(f->xf, clust2sect(bp, pstart));
832 if(p == 0){
833 chat("getsect %ld failed\n", pstart);
834 goto error;
835 }
836 xd = (Dosdir *)p->iobuf;
837 dirdump(xd);
838 st = getstart(f->xf, xd);
839 if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st)
840 goto error;
841
842 /*
843 * parent's parent's .. is the next entry, and has start of parent's parent's parent
844 */
845 xd++;
846 dirdump(xd);
847 if(xd->name[0] != '.' || xd->name[1] != '.')
848 goto error;
849 ppstart = getstart(f->xf, xd);
850 putsect(p);
851
852 /*
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
855 */
856 ppclust = ppstart;
857 if(f->xf->isfat32 && ppclust == 0){
858 ppclust = bp->rootstart;
859 chat("ppclust 0, resetting to rootstart\n");
860 }
861 k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr;
862 p = getsect(f->xf, k);
863 if(p == nil){
864 chat("getsect %ld failed\n", k);
865 goto error;
866 }
867 xd = (Dosdir *)p->iobuf;
868 dirdump(xd);
869 if(ppstart){
870 st = getstart(f->xf, xd);
871 if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st)
872 goto error;
873 }
874 for(so=1;; so++){
875 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
876 xd = (Dosdir *)&p->iobuf[o];
877 if(xd->name[0] == 0x00){
878 chat("end dir\n");
879 goto error;
880 }
881 if(xd->name[0] == DOSEMPTY)
882 continue;
883 st = getstart(f->xf, xd);
884 if(st == pstart)
885 goto out;
886 }
887 if(ppclust){
888 if(so%bp->clustsize == 0){
889 mlock(bp);
890 ppclust = getfat(f->xf, ppclust);
891 unmlock(bp);
892 if(ppclust < 0){
893 chat("getfat %ld failed\n", ppclust);
894 goto error;
895 }
896 }
897 k = clust2sect(bp, ppclust) +
898 so%bp->clustsize;
899 }else{
900 if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
901 goto error;
902 k = bp->rootaddr + so;
903 }
904 putsect(p);
905 p = getsect(f->xf, k);
906 if(p == 0){
907 chat("getsect %ld failed\n", k);
908 goto error;
909 }
910 }
911 out:
912 putsect(p);
913 ndp->paddr = k;
914 ndp->poffset = o;
915 return 0;
916
917 error:
918 if(p)
919 putsect(p);
920 return -1;
921 }
922
923 long
924 readfile(Xfile *f, void *vbuf, long offset, long count)
925 {
926 Xfs *xf = f->xf;
927 Dosbpb *bp = xf->ptr;
928 Dosptr *dp = f->ptr;
929 Dosdir *d = dp->d;
930 int isect, addr, o, c;
931 Iosect *p;
932 uchar *buf;
933 long length, rcnt;
934
935 rcnt = 0;
936 length = GLONG(d->length);
937 buf = vbuf;
938 if(offset >= length)
939 return 0;
940 if(offset+count >= length)
941 count = length - offset;
942 isect = offset/bp->sectsize;
943 o = offset%bp->sectsize;
944 while(count > 0){
945 addr = fileaddr(f, isect++, 0);
946 if(addr < 0)
947 break;
948 c = bp->sectsize - o;
949 if(c > count)
950 c = count;
951 p = getsect(xf, addr);
952 if(p == 0)
953 return -1;
954 memmove(&buf[rcnt], &p->iobuf[o], c);
955 putsect(p);
956 count -= c;
957 rcnt += c;
958 o = 0;
959 }
960 return rcnt;
961 }
962
963 long
964 writefile(Xfile *f, void *vbuf, long offset, long count)
965 {
966 Xfs *xf = f->xf;
967 Dosbpb *bp = xf->ptr;
968 Dosptr *dp = f->ptr;
969 Dosdir *d = dp->d;
970 int isect, addr = 0, o, c;
971 Iosect *p;
972 uchar *buf;
973 long length, rcnt = 0, dlen;
974
975 buf = vbuf;
976 isect = offset/bp->sectsize;
977 o = offset%bp->sectsize;
978 while(count > 0){
979 addr = fileaddr(f, isect++, 1);
980 if(addr < 0)
981 break;
982 c = bp->sectsize - o;
983 if(c > count)
984 c = count;
985 if(c == bp->sectsize){
986 p = getosect(xf, addr);
987 p->flags = 0;
988 }else{
989 p = getsect(xf, addr);
990 if(p == nil)
991 return -1;
992 }
993 memmove(&p->iobuf[o], &buf[rcnt], c);
994 p->flags |= BMOD;
995 putsect(p);
996 count -= c;
997 rcnt += c;
998 o = 0;
999 }
1000 if(rcnt <= 0 && addr < 0)
1001 return -1;
1002 length = 0;
1003 dlen = GLONG(d->length);
1004 if(rcnt > 0)
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;
1010 }
1011 if(length > dlen)
1012 PLONG(d->length, length);
1013 puttime(d, 0);
1014 dp->p->flags |= BMOD;
1015 return rcnt;
1016 }
1017
1018 int
1019 truncfile(Xfile *f, long length)
1020 {
1021 Xfs *xf = f->xf;
1022 Dosbpb *bp = xf->ptr;
1023 Dosptr *dp = f->ptr;
1024 Dosdir *d = dp->d;
1025 long clust, next, n;
1026
1027 mlock(bp);
1028 clust = getstart(f->xf, d);
1029 n = length;
1030 if(n <= 0)
1031 putstart(f->xf, d, 0);
1032 else
1033 n -= bp->sectsize;
1034 while(clust > 0){
1035 next = getfat(xf, clust);
1036 if(n <= 0)
1037 putfat(xf, clust, 0);
1038 else
1039 n -= bp->clustsize*bp->sectsize;
1040 clust = next;
1041 }
1042 unmlock(bp);
1043 PLONG(d->length, length);
1044 dp->iclust = 0;
1045 dp->clust = 0;
1046 dp->p->flags |= BMOD;
1047 return 0;
1048 }
1049
1050 void
1051 putdir(Dosdir *d, Dir *dp)
1052 {
1053 if(dp->mode != ~0){
1054 if(dp->mode & 2)
1055 d->attr &= ~DRONLY;
1056 else
1057 d->attr |= DRONLY;
1058 if(dp->mode & DMEXCL)
1059 d->attr |= DSYSTEM;
1060 else
1061 d->attr &= ~DSYSTEM;
1062 }
1063 if(dp->mtime != ~0)
1064 puttime(d, dp->mtime);
1065 }
1066
1067 /*
1068 * should extend this to deal with
1069 * creation and access dates
1070 */
1071 void
1072 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset)
1073 {
1074 if(d == nil || addr == 0)
1075 panic("getdir on root");
1076 dp->type = 0;
1077 dp->dev = 0;
1078 getname(dp->name, d);
1079
1080 dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1081 offset/DOSDIRSIZE;
1082 dp->qid.vers = 0;
1083
1084 if(d->attr & DRONLY)
1085 dp->mode = 0444;
1086 else
1087 dp->mode = 0666;
1088 dp->atime = gtime(d);
1089 dp->mtime = dp->atime;
1090 dp->qid.type = QTFILE;
1091 if(d->attr & DDIR){
1092 dp->qid.type = QTDIR;
1093 dp->mode |= DMDIR|0111;
1094 dp->length = 0;
1095 }else
1096 dp->length = GLONG(d->length);
1097 if(d->attr & DSYSTEM){
1098 dp->mode |= DMEXCL;
1099 if(iscontig(xfs, d))
1100 dp->mode |= DMAPPEND;
1101 }
1102
1103 dp->uid = "bill";
1104 dp->muid = "bill";
1105 dp->gid = "trog";
1106 }
1107
1108 void
1109 getname(char *p, Dosdir *d)
1110 {
1111 int c, i;
1112
1113 for(i=0; i<8; i++){
1114 c = d->name[i];
1115 if(c == '\0' || c == ' ')
1116 break;
1117 if(i == 0 && c == 0x05)
1118 c = 0xe5;
1119 *p++ = c;
1120 }
1121 for(i=0; i<3; i++){
1122 c = d->ext[i];
1123 if(c == '\0' || c == ' ')
1124 break;
1125 if(i == 0)
1126 *p++ = '.';
1127 *p++ = c;
1128 }
1129 *p = 0;
1130 }
1131
1132 static char*
1133 getnamerunes(char *dst, uchar *buf, int step)
1134 {
1135 int i;
1136 Rune r;
1137 char dbuf[DOSRUNE * UTFmax + 1], *d;
1138
1139 d = dbuf;
1140 r = 1;
1141 for(i = 1; r && i < 11; i += 2){
1142 r = buf[i] | (buf[i+1] << 8);
1143 d += runetochar(d, &r);
1144 }
1145 for(i = 14; r && i < 26; i += 2){
1146 r = buf[i] | (buf[i+1] << 8);
1147 d += runetochar(d, &r);
1148 }
1149 for(i = 28; r && i < 32; i += 2){
1150 r = buf[i] | (buf[i+1] << 8);
1151 d += runetochar(d, &r);
1152 }
1153
1154 if(step == 1)
1155 dst -= d - dbuf;
1156
1157 memmove(dst, dbuf, d - dbuf);
1158
1159 if(step == -1){
1160 dst += d - dbuf;
1161 *dst = '\0';
1162 }
1163
1164 return dst;
1165 }
1166
1167 char*
1168 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1169 {
1170 /*
1171 * validation checks to make sure we're
1172 * making up a consistent name
1173 */
1174 if(buf[11] != 0xf || buf[12] != 0){
1175 *islong = 0;
1176 return nil;
1177 }
1178 if(step == 1){
1179 if((buf[0] & 0xc0) == 0x40){
1180 *islong = buf[0] & 0x3f;
1181 *sum = buf[13];
1182 d = dbuf + DOSNAMELEN;
1183 *--d = '\0';
1184 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1185 *islong = buf[0];
1186 }else{
1187 *islong = 0;
1188 return nil;
1189 }
1190 }else{
1191 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1192 *islong = buf[0] & 0x3f;
1193 if(buf[0] & 0x40)
1194 *sum = -1;
1195 }else{
1196 *islong = 0;
1197 *sum = -1;
1198 return nil;
1199 }
1200 }
1201 if(*islong > 20){
1202 *islong = 0;
1203 *sum = -1;
1204 return nil;
1205 }
1206
1207 return getnamerunes(d, buf, step);
1208 }
1209
1210 void
1211 putname(char *p, Dosdir *d)
1212 {
1213 int i;
1214
1215 memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1216 for(i=0; i<sizeof d->name; i++){
1217 if(*p == 0 || *p == '.')
1218 break;
1219 d->name[i] = toupper(*p++);
1220 }
1221 p = strrchr(p, '.');
1222 if(p){
1223 for(i=0; i<sizeof d->ext; i++){
1224 if(*++p == 0)
1225 break;
1226 d->ext[i] = toupper(*p);
1227 }
1228 }
1229 }
1230
1231 static void
1232 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1233 {
1234 Rune r;
1235 Dosdir ds;
1236 int i, j;
1237
1238 memset(&ds, 0xff, sizeof ds);
1239 ds.attr = 0xf;
1240 ds.reserved[0] = 0;
1241 ds.reserved[1] = sum;
1242 ds.start[0] = 0;
1243 ds.start[1] = 0;
1244 if(first)
1245 ds.name[0] = 0x40 | curslot;
1246 else
1247 ds.name[0] = curslot;
1248 memmove(slot, &ds, sizeof ds);
1249
1250 j = (curslot-1) * DOSRUNE;
1251
1252 for(i = 1; i < 11; i += 2){
1253 r = longname[j++];
1254 slot[i] = r;
1255 slot[i+1] = r >> 8;
1256 if(r == 0)
1257 return;
1258 }
1259 for(i = 14; i < 26; i += 2){
1260 r = longname[j++];
1261 slot[i] = r;
1262 slot[i+1] = r >> 8;
1263 if(r == 0)
1264 return;
1265 }
1266 for(i = 28; i < 32; i += 2){
1267 r = longname[j++];
1268 slot[i] = r;
1269 slot[i+1] = r >> 8;
1270 if(r == 0)
1271 return;
1272 }
1273 }
1274
1275 int
1276 aliassum(Dosdir *d)
1277 {
1278 int i, sum;
1279
1280 if(d == nil)
1281 return -1;
1282 sum = 0;
1283 for(i = 0; i < 11; i++)
1284 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1285 return sum & 0xff;
1286 }
1287
1288 int
1289 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1290 {
1291 Dosbpb *bp;
1292 Dosdir tmpd;
1293 Rune longname[DOSNAMELEN+1];
1294 int i, first, sum, nds, len;
1295
1296 /* calculate checksum */
1297 putname(sname, &tmpd);
1298 sum = aliassum(&tmpd);
1299
1300 bp = xf->ptr;
1301 first = 1;
1302 len = utftorunes(longname, name, DOSNAMELEN);
1303 if(chatty){
1304 chat("utftorunes %s =", name);
1305 for(i=0; i<len; i++)
1306 chat(" %.4X", longname[i]);
1307 chat("\n");
1308 }
1309 for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1310 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1311 first = 0;
1312 ndp->offset += 32;
1313 if(ndp->offset == bp->sectsize){
1314 chat("long name moving over sector boundary\n");
1315 ndp->p->flags |= BMOD;
1316 putsect(ndp->p);
1317 ndp->p = nil;
1318
1319 /*
1320 * switch to the next cluster for a long entry
1321 * naddr should be set up correctly by searchdir
1322 */
1323 ndp->prevaddr = ndp->addr;
1324 ndp->addr = ndp->naddr;
1325 ndp->naddr = -1;
1326 if(ndp->addr == -1)
1327 return -1;
1328 ndp->p = getsect(xf, ndp->addr);
1329 if(ndp->p == nil)
1330 return -1;
1331 ndp->offset = 0;
1332 ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1333 }
1334 }
1335 return 0;
1336 }
1337
1338 long
1339 getfat(Xfs *xf, int n)
1340 {
1341 Dosbpb *bp = xf->ptr;
1342 Iosect *p;
1343 ulong k, sect;
1344 int o, fb;
1345
1346 if(n < FATRESRV || n >= bp->fatclusters)
1347 return -1;
1348 fb = bp->fatbits;
1349 k = (fb * n) >> 3;
1350 if(k >= bp->fatsize*bp->sectsize)
1351 panic("getfat");
1352 sect = k/bp->sectsize + bp->fataddr;
1353 o = k%bp->sectsize;
1354 p = getsect(xf, sect);
1355 if(p == nil)
1356 return -1;
1357 k = p->iobuf[o++];
1358 if(o >= bp->sectsize){
1359 putsect(p);
1360 p = getsect(xf, sect+1);
1361 if(p == nil)
1362 return -1;
1363 o = 0;
1364 }
1365 k |= p->iobuf[o++]<<8;
1366 if(fb == 32){
1367 /* fat32 is really fat28 */
1368 k |= p->iobuf[o++] << 16;
1369 k |= (p->iobuf[o] & 0x0f) << 24;
1370 fb = 28;
1371 }
1372 putsect(p);
1373 if(fb == 12){
1374 if(n&1)
1375 k >>= 4;
1376 else
1377 k &= 0xfff;
1378 }
1379 if(chatty > 1)
1380 chat("fat(%#x)=%#lx...", n, k);
1381
1382 /*
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.
1387 */
1388 if(k >= (1 << fb) - 8)
1389 return -1;
1390
1391 return k;
1392 }
1393
1394 void
1395 putfat(Xfs *xf, int n, ulong val)
1396 {
1397 Fatinfo *fi;
1398 Dosbpb *bp;
1399 Iosect *p;
1400 ulong k, sect, esect;
1401 int o;
1402
1403 bp = xf->ptr;
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)
1408 panic("putfat");
1409 sect = k/bp->sectsize + bp->fataddr;
1410 esect = sect + bp->nfats * bp->fatsize;
1411 for(; sect<esect; sect+=bp->fatsize){
1412 o = k%bp->sectsize;
1413 p = getsect(xf, sect);
1414 if(p == nil)
1415 continue;
1416 switch(bp->fatbits){
1417 case 12:
1418 if(n&1){
1419 p->iobuf[o] &= 0x0f;
1420 p->iobuf[o++] |= val<<4;
1421 if(o >= bp->sectsize){
1422 p->flags |= BMOD;
1423 putsect(p);
1424 p = getsect(xf, sect+1);
1425 if(p == nil)
1426 continue;
1427 o = 0;
1428 }
1429 p->iobuf[o] = val>>4;
1430 }else{
1431 p->iobuf[o++] = val;
1432 if(o >= bp->sectsize){
1433 p->flags |= BMOD;
1434 putsect(p);
1435 p = getsect(xf, sect+1);
1436 if(p == nil)
1437 continue;
1438 o = 0;
1439 }
1440 p->iobuf[o] &= 0xf0;
1441 p->iobuf[o] |= (val>>8) & 0x0f;
1442 }
1443 break;
1444 case 16:
1445 p->iobuf[o++] = val;
1446 p->iobuf[o] = val>>8;
1447 break;
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);
1453 break;
1454 default:
1455 panic("putfat fatbits");
1456 }
1457 p->flags |= BMOD;
1458 putsect(p);
1459 }
1460
1461 if(val == 0)
1462 bp->freeclusters++;
1463 else
1464 bp->freeclusters--;
1465
1466 if(bp->fatinfo){
1467 p = getsect(xf, bp->fatinfo);
1468 if(p != nil){
1469 fi = (Fatinfo*)p->iobuf;
1470 PLONG(fi->nextfree, bp->freeptr);
1471 PLONG(fi->freeclust, bp->freeclusters);
1472 p->flags |= BMOD;
1473 putsect(p);
1474 }
1475 }
1476 }
1477
1478 /*
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.
1483 */
1484 int
1485 cfalloc(Xfile *f)
1486 {
1487 int l;
1488
1489 if((l=makecontig(f, 8)) >= 0)
1490 return l;
1491 return makecontig(f, 1);
1492 }
1493
1494 /*
1495 * Check whether a file is contiguous.
1496 */
1497 int
1498 iscontig(Xfs *xf, Dosdir *d)
1499 {
1500 long clust, next;
1501
1502 clust = getstart(xf, d);
1503 if(clust <= 0)
1504 return 1;
1505
1506 for(;;) {
1507 next = getfat(xf, clust);
1508 if(next < 0)
1509 return 1;
1510 if(next != clust+1)
1511 return 0;
1512 clust = next;
1513 }
1514 }
1515
1516 /*
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.
1520 */
1521 int
1522 makecontig(Xfile *f, int nextra)
1523 {
1524 Dosbpb *bp;
1525 Dosdir *d;
1526 Dosptr *dp;
1527 Xfs *xf;
1528 Iosect *wp, *rp;
1529 long clust, next, last, start, rclust, wclust, eclust, ostart;
1530 int isok, i, n, nclust, nrun, rs, ws;
1531
1532 xf = f->xf;
1533 bp = xf->ptr;
1534 dp = f->ptr;
1535 d = dp->d;
1536
1537 isok = 1;
1538 nclust = 0;
1539 clust = fileclust(f, 0, 0);
1540 chat("clust %#lux", clust);
1541 if(clust != -1) {
1542 for(;;) {
1543 nclust++;
1544 chat(".");
1545 next = getfat(xf, clust);
1546 if(next <= 0)
1547 break;
1548 if(next != clust+1)
1549 isok = 0;
1550 clust = next;
1551 }
1552 }
1553 chat("nclust %d\n", nclust);
1554
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)
1560 break;
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 */
1567 return eclust;
1568 }
1569 }
1570
1571 /* need to search for nclust+nextra contiguous free blocks */
1572 last = -1;
1573 n = bp->freeptr;
1574 nrun = 0;
1575 for(;;){
1576 if(getfat(xf, n) == 0) {
1577 if(last+1 == n)
1578 nrun++;
1579 else
1580 nrun = 1;
1581 if(nrun >= nclust+nextra)
1582 break;
1583 last = n;
1584 }
1585 if(++n >= bp->fatclusters)
1586 n = FATRESRV;
1587 if(n == bp->freeptr) {
1588 errno = Econtig;
1589 return -1;
1590 }
1591 }
1592 bp->freeptr = n+1;
1593
1594 /* copy old data over */
1595 start = n+1 - nrun;
1596
1597 /* sanity check */
1598 for(i=0; i<nclust+nextra; i++)
1599 assert(getfat(xf, start+i) == 0);
1600
1601 chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1602
1603 wclust = start;
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);
1609 if(rp == nil)
1610 return -1;
1611 wp = getosect(xf, ws);
1612 assert(wp != nil);
1613 memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1614 wp->flags = BMOD;
1615 putsect(rp);
1616 putsect(wp);
1617 }
1618 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1619 next = getfat(xf, rclust);
1620 putfat(xf, wclust, wclust+1);
1621 wclust++;
1622 }
1623
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 */
1628
1629 /* update directory entry to point at new start */
1630 ostart = fileclust(f, 0, 0);
1631 putstart(xf, d, start);
1632
1633 /* check our work */
1634 i = 0;
1635 clust = fileclust(f, 0, 0);
1636 if(clust != -1) {
1637 for(;;) {
1638 i++;
1639 next = getfat(xf, clust);
1640 if(next <= 0)
1641 break;
1642 assert(next == clust+1);
1643 clust = next;
1644 }
1645 }
1646 chat("chain check: len %d\n", i);
1647 assert(i == nclust+1);
1648
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 */
1653 }
1654
1655 return start+nclust;
1656 }
1657
1658 int
1659 falloc(Xfs *xf)
1660 {
1661 Dosbpb *bp = xf->ptr;
1662 Iosect *p;
1663 int n, i, k;
1664
1665 n = bp->freeptr;
1666 for(;;){
1667 if(getfat(xf, n) == 0)
1668 break;
1669 if(++n >= bp->fatclusters)
1670 n = FATRESRV;
1671 if(n == bp->freeptr)
1672 return -1;
1673 }
1674 bp->freeptr = n+1;
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);
1682 p->flags = BMOD;
1683 putsect(p);
1684 }
1685 return n;
1686 }
1687
1688 void
1689 ffree(Xfs *xf, long start)
1690 {
1691 putfat(xf, start, 0);
1692 }
1693
1694 long
1695 clust2sect(Dosbpb *bp, long clust)
1696 {
1697 return bp->dataaddr + (clust - FATRESRV) * bp->clustsize;
1698 }
1699
1700 long
1701 sect2clust(Dosbpb *bp, long sect)
1702 {
1703 long c;
1704
1705 c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV;
1706 assert(sect == clust2sect(bp, c));
1707 return c;
1708 }
1709
1710 void
1711 puttime(Dosdir *d, long s)
1712 {
1713 Tm *t;
1714 ushort x;
1715
1716 if(s == 0)
1717 s = time(0);
1718 t = localtime(s);
1719
1720 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1721 PSHORT(d->time, x);
1722 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1723 PSHORT(d->date, x);
1724 }
1725
1726 long
1727 gtime(Dosdir *dp)
1728 {
1729 Tm tm;
1730 int i;
1731
1732 i = GSHORT(dp->time);
1733 tm.hour = i >> 11;
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;
1739 tm.mday = i & 31;
1740 tm.zone[0] = '\0';
1741 tm.tzoff = 0;
1742 tm.yday = 0;
1743
1744 return tm2sec(&tm);
1745 }
1746
1747 /*
1748 * structure dumps for debugging
1749 */
1750 void
1751 bootdump(int fd, Dosboot *b)
1752 {
1753 Biobuf bp;
1754
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);
1776 Bterm(&bp);
1777 }
1778
1779 void
1780 bootdump32(int fd, Dosboot32 *b)
1781 {
1782 Biobuf bp;
1783
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]);
1810 Bterm(&bp);
1811 }
1812
1813 void
1814 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1815 {
1816 Fatinfo *fi;
1817 Iosect *p1;
1818 int fisec, bsec, res;
1819
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);
1826 if(p1 == nil)
1827 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1828 else{
1829 fprint(fd, "\nbackup boot\n");
1830 bootdump32(fd, (Dosboot32*)p1->iobuf);
1831 putsect(p1);
1832 }
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);
1838 if(p1 == nil)
1839 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1840 else{
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));
1848 putsect(p1);
1849 }
1850 }else if(fisec != 0xffff)
1851 fprint(2, "bad fat info sector: %d reserved %d\n", bsec, res);
1852 }
1853
1854 void
1855 dirdump(void *vdbuf)
1856 {
1857 static char attrchar[] = "rhsvda67";
1858 Dosdir *d;
1859 char *name, namebuf[DOSNAMELEN];
1860 char buf[128], *s, *ebuf;
1861 uchar *dbuf;
1862 int i;
1863
1864 if(!chatty)
1865 return;
1866
1867 d = vdbuf;
1868
1869 ebuf = buf + sizeof(buf);
1870 if(d->attr == 0xf){
1871 dbuf = vdbuf;
1872 name = namebuf + DOSNAMELEN;
1873 *--name = '\0';
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));
1876 }else{
1877 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1878 for(i=7; i>=0; i--)
1879 *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1880
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);
1885
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);
1890
1891 i = GSHORT(d->adate);
1892 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1893
1894 seprint(s, ebuf, " %d %d", GSHORT(d->start), GSHORT(d->length));
1895 }
1896 chat("%s\n", buf);
1897 }
1898
1899 int
1900 cistrcmp(char *s1, char *s2)
1901 {
1902 int c1, c2;
1903
1904 while(*s1){
1905 c1 = *s1++;
1906 c2 = *s2++;
1907
1908 if(c1 >= 'A' && c1 <= 'Z')
1909 c1 -= 'A' - 'a';
1910
1911 if(c2 >= 'A' && c2 <= 'Z')
1912 c2 -= 'A' - 'a';
1913
1914 if(c1 != c2)
1915 return c1 - c2;
1916 }
1917 return -*s2;
1918 }
1919
1920 int
1921 utftorunes(Rune *rr, char *s, int n)
1922 {
1923 Rune *r, *re;
1924 int c;
1925
1926 r = rr;
1927 re = r + n - 1;
1928 while(c = (uchar)*s){
1929 if(c < Runeself){
1930 *r = c;
1931 s++;
1932 }else
1933 s += chartorune(r, s);
1934 r++;
1935 if(r >= re)
1936 break;
1937 }
1938 *r = 0;
1939 return r - rr;
1940 }
1941