]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/dossrv/dossubs.c
rio, kbdfs: increase read buffer for high latency kbdfs support
[plan9front.git] / sys / src / cmd / dossrv / dossubs.c
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
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
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 == nil)
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",
148                                                 bp->freeclusters, bp->freeptr);
149                                 }
150                                 putsect(p1);
151                         }
152                 }
153         }else{
154                 if(chatty)
155                         bootdump(2, b);
156                 bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
157                 bp->rootstart = 0;
158                 bp->dataaddr = bp->rootaddr + (bp->rootsize*DOSDIRSIZE + bp->sectsize-1)/bp->sectsize;
159                 bp->freeptr = FATRESRV;
160         }
161         bp->fatclusters = FATRESRV + (bp->volsize - bp->dataaddr)/bp->clustsize;
162
163         if(xf->isfat32)
164                 bp->fatbits = 32;
165         else if(bp->fatclusters < 4087)
166                 bp->fatbits = 12;
167         else
168                 bp->fatbits = 16;
169
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);
175         putsect(p);
176         return 0;
177 }
178
179 /*
180  * initialize f to the root directory
181  * this file has no Dosdir entry,
182  * so we special case it all over.
183  */
184 void
185 rootfile(Xfile *f)
186 {
187         Dosptr *dp;
188
189         dp = f->ptr;
190         memset(dp, 0, sizeof(Dosptr));
191         dp->prevaddr = -1;
192 }
193
194 int
195 isroot(vlong addr)
196 {
197         return addr == 0;
198 }
199
200 int
201 getfile(Xfile *f)
202 {
203         Dosptr *dp;
204         Iosect *p;
205
206         dp = f->ptr;
207         if(dp->p != nil)
208                 panic("getfile");
209         p = getsect(f->xf, dp->addr);
210         if(p == nil)
211                 return -1;
212
213         /*
214          * we could also make up a Dosdir for the root
215          */
216         dp->d = nil;
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));
220                         putsect(p);
221                         errno = Enonexist;
222                         return -1;
223                 }
224                 dp->d = (Dosdir *)&p->iobuf[dp->offset];
225         }
226         dp->p = p;
227         return 0;
228 }
229
230 void
231 putfile(Xfile *f)
232 {
233         Dosptr *dp;
234
235         dp = f->ptr;
236         if(dp->p == nil)
237                 panic("putfile");
238         putsect(dp->p);
239         dp->p = nil;
240         dp->d = nil;
241 }
242
243 long
244 getstart(Xfs *xf, Dosdir *d)
245 {
246         long start;
247
248         start = GSHORT(d->start);
249         if(xf->isfat32)
250                 start |= GSHORT(d->hstart)<<16;
251         return start;
252 }
253
254 void
255 putstart(Xfs *xf, Dosdir *d, long start)
256 {
257         PSHORT(d->start, start);
258         if(xf->isfat32)
259                 PSHORT(d->hstart, start>>16);
260 }
261
262 /*
263  * return the disk cluster for the iclust cluster in f
264  */
265 long
266 fileclust(Xfile *f, long iclust, int cflag)
267 {
268         Dosbpb *bp;
269         Dosptr *dp;
270         Dosdir *d;
271         long start, clust, nskip, next;
272
273         bp = f->xf->ptr;
274         dp = f->ptr;
275         d = dp->d;
276         next = 0;
277
278         /* 
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.
282          */
283         if(!f->xf->isfat32 && isroot(dp->addr))
284                 return -1;
285
286         if(f->xf->isfat32 && isroot(dp->addr)){
287                 start = bp->rootstart;
288         }else{
289                 start = getstart(f->xf, d);
290                 if(start == 0){
291                         if(!cflag)
292                                 return -1;
293                         mlock(bp);
294                         start = falloc(f->xf);
295                         unmlock(bp);
296                         if(start <= 0)
297                                 return -1;
298                         puttime(d, 0);
299                         putstart(f->xf, d, start);
300                         dp->p->flags |= BMOD;
301                         dp->clust = 0;
302                 }
303         }
304         if(dp->clust == 0 || iclust < dp->iclust){
305                 clust = start;
306                 nskip = iclust;
307         }else{
308                 clust = dp->clust;
309                 nskip = iclust - dp->iclust;
310         }
311         if(chatty > 1 && nskip > 0)
312                 chat("clust %#lx, skip %ld...", clust, nskip);
313         if(clust <= 0)
314                 return -1;
315         if(nskip > 0){
316                 mlock(bp);
317                 while(--nskip >= 0){
318                         next = getfat(f->xf, clust);
319                         if(chatty > 1)
320                                 chat("->%#lx", next);
321                         if(next > 0){
322                                 clust = next;
323                                 continue;
324                         }else if(!cflag)
325                                 break;
326                         if(d && (d->attr&DSYSTEM)){
327                                 next = cfalloc(f);
328                                 if(next < 0)
329                                         break;
330                                 /* cfalloc will call putfat for us, since clust may change */
331                         } else {
332                                 next = falloc(f->xf);
333                                 if(next < 0)
334                                         break;
335                                 putfat(f->xf, clust, next);
336                         }
337                         clust = next;
338                 }
339                 unmlock(bp);
340                 if(next <= 0)
341                         return -1;
342                 dp->clust = clust;
343                 dp->iclust = iclust;
344         }
345         if(chatty > 1)
346                 chat(" clust(%#lx)=%#lx...", iclust, clust);
347         return clust;
348 }
349
350 /*
351  * return the disk sector for the isect disk sector in f 
352  */
353 vlong
354 fileaddr(Xfile *f, long isect, int cflag)
355 {
356         Dosbpb *bp;
357         Dosptr *dp;
358         long clust;
359
360         bp = f->xf->ptr;
361         dp = f->ptr;
362         if(!f->xf->isfat32 && isroot(dp->addr)){
363                 if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
364                         return -1;
365                 return bp->rootaddr + isect;
366         }
367         clust = fileclust(f, isect/bp->clustsize, cflag);
368         if(clust < 0)
369                 return -1;
370
371         return clust2sect(bp, clust) + isect%bp->clustsize;
372 }
373
374 /*
375  * translate names
376  */
377 void
378 fixname(char *buf)
379 {
380         int c;
381         char *p;
382
383         p = buf;
384         while(c = *p){
385                 if(c == ':' && trspaces)
386                         *p = ' ';
387                 p++;
388         }
389 }
390
391 /*
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
396  *      Long - long name 
397  */
398 int
399 classifyname(char *buf)
400 {
401         char *p, *dot;
402         int c, isextended, is8dot3, islower, ndot;
403
404         p = buf;
405         isextended = 0;
406         islower = 0;
407         dot = nil;
408         ndot = 0;
409         while(c = (uchar)*p){
410                 if(c&0x80)      /* UTF8 */
411                         isextended = 1;
412                 else if(c == '.'){
413                         dot = p;
414                         ndot++;
415                 }else if(strchr("+,:;=[] ", c))
416                         isextended = 1;
417                 else if(!isdos[c])
418                         return Invalid;
419                 if('a' <= c && c <= 'z')
420                         islower = 1;
421                 p++;
422         }
423
424         is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
425         
426         if(!isextended && is8dot3){
427                 if(islower)
428                         return ShortLower;
429                 return Short;
430         }
431         return Long;
432 }
433                 
434 /*
435  * make an alias for a valid long file name
436  */
437 void
438 mkalias(char *name, char *sname, int id)
439 {
440         Rune r;
441         char *s, *e, sid[10];
442         int i, esuf, v;
443
444         e = strrchr(name, '.');
445         if(e == nil)
446                 e = strchr(name, '\0');
447
448         s = name;
449         i = 0;
450         while(s < e && i < 6){
451                 if(isdos[(uchar)*s])
452                         sname[i++] = *s++;
453                 else
454                         s += chartorune(&r, s);
455         }
456
457         v = snprint(sid, 10, "%d", id);
458         if(i + 1 + v > 8)
459                 i = 8 - 1 - v;
460         sname[i++] = '~';
461         strcpy(&sname[i], sid);
462         i += v;
463
464         sname[i++] = '.';
465         esuf = i + 3;
466         if(esuf > 12)
467                 panic("bad mkalias");
468         while(*e && i < esuf){
469                 if(isdos[(uchar)*e])
470                         sname[i++] = *e++;
471                 else
472                         e += chartorune(&r, e);
473         }
474         if(sname[i-1] == '.')
475                 i--;
476         sname[i] = '\0';
477 }
478
479 /*
480  * check for valid plan 9 names,
481  * rewrite ' ' to ':'
482  */
483 char isfrog[256]={
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 */
489         ['/']   1,
490         [0x7f]  1,
491 };
492
493 int
494 nameok(char *elem)
495 {
496         while(*elem) {
497                 if(*elem == ' ' && trspaces)
498                         *elem = ':';
499                 if(isfrog[*(uchar*)elem])
500                         return 0;
501                 elem++;
502         }
503         return 1;
504 }
505
506 /*
507  * look for a directory entry matching name
508  * always searches for long names which match a short name
509  */
510 int
511 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype)
512 {
513         Xfs *xf;
514         Iosect *p;
515         Dosbpb *bp;
516         Dosdir *d;
517         char buf[261], *bname;
518         int isect, o, o1, islong, have, need, sum;
519         vlong addr, addr1, addr2, prevaddr, prevaddr1;
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 == nil)
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, o;
646         vlong addr;
647         Iosect *p;
648         Dosdir *d;
649
650         for(isect=0;; isect++){
651                 addr = fileaddr(f, isect, 0);
652                 if(addr < 0)
653                         break;
654                 p = getsect(xf, addr);
655                 if(p == nil)
656                         return -1;
657                 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
658                         d = (Dosdir *)&p->iobuf[o];
659                         if(d->name[0] == 0x00){
660                                 putsect(p);
661                                 return 0;
662                         }
663                         if(d->name[0] == DOSEMPTY)
664                                 continue;
665                         if(d->name[0] == '.')
666                                 continue;
667                         if(d->attr&DVLABEL)
668                                 continue;
669                         putsect(p);
670                         return -1;
671                 }
672                 putsect(p);
673         }
674         return 0;
675 }
676
677 long
678 readdir(Xfile *f, void *vbuf, vlong offset, long count)
679 {
680         Xfs *xf;
681         Dosbpb *bp;
682         Dir dir;
683         int isect, o, islong, sum;
684         vlong addr;
685         Iosect *p;
686         Dosdir *d;
687         long rcnt, n;
688         char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN];
689         uchar *buf;
690
691         buf = vbuf;
692         rcnt = 0;
693         xf = f->xf;
694         bp = xf->ptr;
695         if(count <= 0)
696                 return 0;
697         islong = 0;
698         sum = -1;
699         name = nil;
700         for(isect=0;; isect++){
701                 addr = fileaddr(f, isect, 0);
702                 if(addr < 0)
703                         break;
704                 p = getsect(xf, addr);
705                 if(p == nil)
706                         return -1;
707                 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
708                         d = (Dosdir *)&p->iobuf[o];
709                         if(d->name[0] == 0x00){
710                                 putsect(p);
711                                 return rcnt;
712                         }
713                         if(d->name[0] == DOSEMPTY)
714                                 continue;
715                         dirdump(d);
716                         if(d->name[0] == '.'){
717                                 if(d->name[1] == ' ' || d->name[1] == 0)
718                                         continue;
719                                 if(d->name[1] == '.' &&
720                                   (d->name[2] == ' ' || d->name[2] == 0))
721                                         continue;
722                         }
723                         if((d->attr & 0xf) == 0xf){
724                                 name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1);
725                                 continue;
726                         }
727                         if(d->attr & DVLABEL){
728                                 islong = 0;
729                                 continue;
730                         }
731                         dir.name = snamebuf;
732                         getdir(xf, &dir, d, addr, o);
733                         if(islong == 1 && nameok(name) && sum == aliassum(d))
734                                 dir.name = name;
735                         islong = 0;
736                         n = convD2M(&dir, &buf[rcnt], count - rcnt);
737                         name = nil;
738                         if(n <= BIT16SZ){       /* no room for next entry */
739                                 putsect(p);
740                                 return rcnt;
741                         }
742                         rcnt += n;
743                         if(offset > 0){
744                                 offset -= rcnt;
745                                 rcnt = 0;
746                                 islong = 0;
747                                 continue;
748                         }
749                         if(rcnt == count){
750                                 putsect(p);
751                                 return rcnt;
752                         }
753                 }
754                 putsect(p);
755         }
756         return rcnt;
757 }
758
759 /*
760  * set up ndp for a directory's parent
761  * the hardest part is setting up paddr
762  */
763 int
764 walkup(Xfile *f, Dosptr *ndp)
765 {
766         Dosbpb *bp;
767         Dosptr *dp;
768         Dosdir *xd;
769         Iosect *p;
770         long o, so, start, pstart, ppstart, st, ppclust;
771         vlong k;
772
773         bp = f->xf->ptr;
774         dp = f->ptr;
775         memset(ndp, 0, sizeof(Dosptr));
776         ndp->prevaddr = -1;
777         ndp->naddr = -1;
778         ndp->addr = dp->paddr;
779         ndp->offset = dp->poffset;
780
781         chat("walkup: paddr=%#llx...", dp->paddr);
782
783         /*
784          * root's paddr is always itself
785          */
786         if(isroot(dp->paddr))
787                 return 0;
788
789         /*
790          * find the start of our parent's directory
791          */
792         p = getsect(f->xf, dp->paddr);
793         if(p == nil)
794                 goto error;
795         xd = (Dosdir *)&p->iobuf[dp->poffset];
796         dirdump(xd);
797         start = getstart(f->xf, xd);
798         chat("start=%#lx...", start);
799         if(start == 0)
800                 goto error;
801         putsect(p);
802
803         /*
804          * verify that parent's . points to itself
805          */
806         p = getsect(f->xf, clust2sect(bp, start));
807         if(p == nil)
808                 goto error;
809         xd = (Dosdir *)p->iobuf;
810         dirdump(xd);
811         st = getstart(f->xf, xd);
812         if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st)
813                 goto error;
814
815         /*
816          * parent's .. is the next entry, and has start of parent's parent
817          */
818         xd++;
819         dirdump(xd);
820         if(xd->name[0] != '.' || xd->name[1] != '.')
821                 goto error;
822         pstart = getstart(f->xf, xd);
823         putsect(p);
824
825         /*
826          * we're done if parent is root
827          */
828         if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart)
829                 return 0;
830
831         /*
832          * verify that parent's . points to itself
833          */
834         p = getsect(f->xf, clust2sect(bp, pstart));
835         if(p == nil){
836                 chat("getsect %ld failed\n", pstart);
837                 goto error;
838         }
839         xd = (Dosdir *)p->iobuf;
840         dirdump(xd);
841         st = getstart(f->xf, xd);
842         if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st)
843                 goto error;
844
845         /*
846          * parent's parent's .. is the next entry, and has start of parent's parent's parent
847          */
848         xd++;
849         dirdump(xd);
850         if(xd->name[0] != '.' || xd->name[1] != '.')
851                 goto error;
852         ppstart = getstart(f->xf, xd);
853         putsect(p);
854
855         /*
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
858          */
859         ppclust = ppstart;
860         if(f->xf->isfat32 && ppclust == 0){
861                 ppclust = bp->rootstart;
862                 chat("ppclust 0, resetting to rootstart\n");
863         }
864         k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr;
865         p = getsect(f->xf, k);
866         if(p == nil){
867                 chat("getsect %lld failed\n", k);
868                 goto error;
869         }
870         xd = (Dosdir *)p->iobuf;
871         dirdump(xd);
872         if(ppstart){
873                 st = getstart(f->xf, xd);
874                 if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st)
875                         goto error;
876         }
877         for(so=1;; so++){
878                 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
879                         xd = (Dosdir *)&p->iobuf[o];
880                         if(xd->name[0] == 0x00){
881                                 chat("end dir\n");
882                                 goto error;
883                         }
884                         if(xd->name[0] == DOSEMPTY)
885                                 continue;
886                         st = getstart(f->xf, xd);
887                         if(st == pstart)
888                                 goto out;
889                 }
890                 if(ppclust){
891                         if(so%bp->clustsize == 0){
892                                 mlock(bp);
893                                 ppclust = getfat(f->xf, ppclust);
894                                 unmlock(bp);
895                                 if(ppclust < 0){
896                                         chat("getfat %ld failed\n", ppclust);
897                                         goto error;
898                                 }
899                         }
900                         k = clust2sect(bp, ppclust) + so%bp->clustsize;
901                 }else{
902                         if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
903                                 goto error;
904                         k = bp->rootaddr + so;
905                 }
906                 putsect(p);
907                 p = getsect(f->xf, k);
908                 if(p == nil){
909                         chat("getsect %lld failed\n", k);
910                         goto error;
911                 }
912         }
913 out:
914         putsect(p);
915         ndp->paddr = k;
916         ndp->poffset = o;
917         return 0;
918
919 error:
920         if(p != nil)
921                 putsect(p);
922         return -1;
923 }
924
925 long
926 readfile(Xfile *f, void *vbuf, vlong offset, long count)
927 {
928         Xfs *xf = f->xf;
929         Dosbpb *bp = xf->ptr;
930         Dosptr *dp = f->ptr;
931         Dosdir *d = dp->d;
932         int isect, o, c;
933         vlong addr;
934         Iosect *p;
935         uchar *buf;
936         ulong length, rcnt;
937
938         if(offset >= MAXFILELEN)
939                 return 0;
940         if(offset+count > MAXFILELEN)
941                 count = MAXFILELEN - offset;
942
943         rcnt = 0;
944         length = GLONG(d->length);
945         buf = vbuf;
946         if(offset >= length)
947                 return 0;
948         if(offset+count >= length)
949                 count = length - offset;
950         isect = offset/bp->sectsize;
951         o = offset%bp->sectsize;
952         while(count > 0){
953                 addr = fileaddr(f, isect++, 0);
954                 if(addr < 0)
955                         break;
956                 c = bp->sectsize - o;
957                 if(c > count)
958                         c = count;
959                 p = getsect(xf, addr);
960                 if(p == nil)
961                         return -1;
962                 memmove(&buf[rcnt], &p->iobuf[o], c);
963                 putsect(p);
964                 count -= c;
965                 rcnt += c;
966                 o = 0;
967         }
968         return rcnt;
969 }
970
971 long
972 writefile(Xfile *f, void *vbuf, vlong offset, long count)
973 {
974         Xfs *xf = f->xf;
975         Dosbpb *bp = xf->ptr;
976         Dosptr *dp = f->ptr;
977         Dosdir *d = dp->d;
978         int isect, o, c;
979         vlong addr;
980         Iosect *p;
981         uchar *buf;
982         ulong length, rcnt, dlen;
983
984         if(offset >= MAXFILELEN)
985                 return 0;
986         if(offset+count > MAXFILELEN)
987                 count = MAXFILELEN - offset;
988
989         rcnt = 0;
990         addr = 0;
991         buf = vbuf;
992         isect = offset/bp->sectsize;
993         o = offset%bp->sectsize;
994         while(count > 0){
995                 addr = fileaddr(f, isect++, 1);
996                 if(addr < 0)
997                         break;
998                 c = bp->sectsize - o;
999                 if(c > count)
1000                         c = count;
1001                 if(c == bp->sectsize){
1002                         p = getosect(xf, addr);
1003                         p->flags = 0;
1004                 }else{
1005                         p = getsect(xf, addr);
1006                         if(p == nil)
1007                                 return -1;
1008                 }
1009                 memmove(&p->iobuf[o], &buf[rcnt], c);
1010                 p->flags |= BMOD;
1011                 putsect(p);
1012                 count -= c;
1013                 rcnt += c;
1014                 o = 0;
1015         }
1016         if(addr < 0)
1017                 return -1;
1018         length = 0;
1019         dlen = GLONG(d->length);
1020         if(rcnt > 0)
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;
1026         }
1027         if(length > dlen)
1028                 PLONG(d->length, length);
1029         puttime(d, 0);
1030         dp->p->flags |= BMOD;
1031         return rcnt;
1032 }
1033
1034 int
1035 truncfile(Xfile *f, vlong length)
1036 {
1037         Xfs *xf = f->xf;
1038         Dosbpb *bp = xf->ptr;
1039         Dosptr *dp = f->ptr;
1040         Dosdir *d = dp->d;
1041         long clust, next;
1042         vlong n;
1043
1044         if(length > MAXFILELEN)
1045                 return -1;
1046
1047         mlock(bp);
1048         clust = getstart(f->xf, d);
1049         n = length;
1050         if(n <= 0)
1051                 putstart(f->xf, d, 0);
1052         else
1053                 n -= bp->sectsize;
1054         while(clust > 0){
1055                 next = getfat(xf, clust);
1056                 if(n <= 0)
1057                         putfat(xf, clust, 0);
1058                 else
1059                         n -= bp->clustsize*bp->sectsize;
1060                 clust = next;
1061         }
1062         unmlock(bp);
1063         PLONG(d->length, length);
1064         dp->iclust = 0;
1065         dp->clust = 0;
1066         dp->p->flags |= BMOD;
1067         return 0;
1068 }
1069
1070 void
1071 putdir(Dosdir *d, Dir *dp)
1072 {
1073         if(dp->mode != ~0){
1074                 if(dp->mode & 2)
1075                         d->attr &= ~DRONLY;
1076                 else
1077                         d->attr |= DRONLY;
1078                 if(dp->mode & DMEXCL)
1079                         d->attr |= DSYSTEM;
1080                 else
1081                         d->attr &= ~DSYSTEM;
1082         }
1083         if(dp->mtime != ~0)
1084                 puttime(d, dp->mtime);
1085 }
1086
1087 /*
1088  * should extend this to deal with
1089  * creation and access dates
1090  */
1091 void
1092 getdir(Xfs *xfs, Dir *dp, Dosdir *d, vlong addr, int offset)
1093 {
1094         if(d == nil || addr == 0)
1095                 panic("getdir on root");
1096         dp->type = 0;
1097         dp->dev = 0;
1098         getname(dp->name, d);
1099
1100         dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1101                         offset/DOSDIRSIZE;
1102         dp->qid.vers = 0;
1103
1104         if(d->attr & DRONLY)
1105                 dp->mode = 0444;
1106         else
1107                 dp->mode = 0666;
1108         dp->atime = gtime(d);
1109         dp->mtime = dp->atime;
1110         dp->qid.type = QTFILE;
1111         if(d->attr & DDIR){
1112                 dp->qid.type = QTDIR;
1113                 dp->mode |= DMDIR|0111;
1114                 dp->length = 0;
1115         }else
1116                 dp->length = GLONG(d->length);
1117         if(d->attr & DSYSTEM){
1118                 dp->mode |= DMEXCL;
1119                 if(iscontig(xfs, d))
1120                         dp->mode |= DMAPPEND;
1121         }
1122
1123         dp->uid = "bill";
1124         dp->muid = "bill";
1125         dp->gid = "trog";
1126 }
1127
1128 void
1129 getname(char *p, Dosdir *d)
1130 {
1131         int c, i;
1132
1133         for(i=0; i<8; i++){
1134                 c = d->name[i];
1135                 if(c == '\0' || c == ' ')
1136                         break;
1137                 if(i == 0 && c == 0x05)
1138                         c = 0xe5;
1139                 *p++ = tolower(c);
1140         }
1141         for(i=0; i<3; i++){
1142                 c = d->ext[i];
1143                 if(c == '\0' || c == ' ')
1144                         break;
1145                 if(i == 0)
1146                         *p++ = '.';
1147                 *p++ = tolower(c);
1148         }
1149         *p = 0;
1150 }
1151
1152 static char*
1153 getnamerunes(char *dst, uchar *buf, int step)
1154 {
1155         int i;
1156         Rune r;
1157         char dbuf[DOSRUNE * UTFmax + 1], *d;
1158
1159         d = dbuf;
1160         r = 1;
1161         for(i = 1; r && i < 11; i += 2){
1162                 r = buf[i] | (buf[i+1] << 8);
1163                 d += runetochar(d, &r);
1164         }
1165         for(i = 14; r && i < 26; i += 2){
1166                 r = buf[i] | (buf[i+1] << 8);
1167                 d += runetochar(d, &r);
1168         }
1169         for(i = 28; r && i < 32; i += 2){
1170                 r = buf[i] | (buf[i+1] << 8);
1171                 d += runetochar(d, &r);
1172         }
1173
1174         if(step == 1)
1175                 dst -= d - dbuf;
1176
1177         memmove(dst, dbuf, d - dbuf);
1178
1179         if(step == -1){
1180                 dst += d - dbuf;
1181                 *dst = '\0';
1182         }
1183
1184         return dst;
1185 }
1186
1187 char*
1188 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1189 {
1190         /*
1191          * validation checks to make sure we're
1192          * making up a consistent name
1193          */
1194         if(buf[11] != 0xf || buf[12] != 0){
1195                 *islong = 0;
1196                 return nil;
1197         }
1198         if(step == 1){
1199                 if((buf[0] & 0xc0) == 0x40){
1200                         *islong = buf[0] & 0x3f;
1201                         *sum = buf[13];
1202                         d = dbuf + DOSNAMELEN;
1203                         *--d = '\0';
1204                 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1205                         *islong = buf[0];
1206                 }else{
1207                         *islong = 0;
1208                         return nil;
1209                 }
1210         }else{
1211                 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1212                         *islong = buf[0] & 0x3f;
1213                         if(buf[0] & 0x40)
1214                                 *sum = -1;
1215                 }else{
1216                         *islong = 0;
1217                         *sum = -1;
1218                         return nil;
1219                 }
1220         }
1221         if(*islong > 20){
1222                 *islong = 0;
1223                 *sum = -1;
1224                 return nil;
1225         }
1226
1227         return getnamerunes(d, buf, step);
1228 }
1229
1230 void
1231 putname(char *p, Dosdir *d)
1232 {
1233         int i;
1234
1235         memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1236         for(i=0; i<sizeof d->name; i++){
1237                 if(*p == 0 || *p == '.')
1238                         break;
1239                 d->name[i] = toupper(*p++);
1240         }
1241         p = strrchr(p, '.');
1242         if(p){
1243                 for(i=0; i<sizeof d->ext; i++){
1244                         if(*++p == 0)
1245                                 break;
1246                         d->ext[i] = toupper(*p);
1247                 }
1248         }
1249 }
1250
1251 static void
1252 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1253 {
1254         Rune r;
1255         Dosdir ds;
1256         int i, j;
1257
1258         memset(&ds, 0xff, sizeof ds);
1259         ds.attr = 0xf;
1260         ds.reserved[0] = 0;
1261         ds.reserved[1] = sum;
1262         ds.start[0] = 0;
1263         ds.start[1] = 0;
1264         if(first)
1265                 ds.name[0] = 0x40 | curslot;
1266         else 
1267                 ds.name[0] = curslot;
1268         memmove(slot, &ds, sizeof ds);
1269
1270         j = (curslot-1) * DOSRUNE;
1271
1272         for(i = 1; i < 11; i += 2){
1273                 r = longname[j++];
1274                 slot[i] = r;
1275                 slot[i+1] = r >> 8;
1276                 if(r == 0)
1277                         return;
1278         }
1279         for(i = 14; i < 26; i += 2){
1280                 r = longname[j++];
1281                 slot[i] = r;
1282                 slot[i+1] = r >> 8;
1283                 if(r == 0)
1284                         return;
1285         }
1286         for(i = 28; i < 32; i += 2){
1287                 r = longname[j++];
1288                 slot[i] = r;
1289                 slot[i+1] = r >> 8;
1290                 if(r == 0)
1291                         return;
1292         }
1293 }
1294
1295 int
1296 aliassum(Dosdir *d)
1297 {
1298         int i, sum;
1299
1300         if(d == nil)
1301                 return -1;
1302         sum = 0;
1303         for(i = 0; i < 11; i++)
1304                 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1305         return sum & 0xff;
1306 }
1307
1308 int
1309 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1310 {
1311         Dosbpb *bp;
1312         Dosdir tmpd;
1313         Rune longname[DOSNAMELEN+1];
1314         int i, first, sum, nds, len;
1315
1316         /* calculate checksum */
1317         putname(sname, &tmpd);
1318         sum = aliassum(&tmpd);
1319
1320         bp = xf->ptr;
1321         first = 1;
1322         len = utftorunes(longname, name, DOSNAMELEN);
1323         if(chatty){
1324                 chat("utftorunes %s =", name);
1325                 for(i=0; i<len; i++)
1326                         chat(" %.4X", longname[i]);
1327                 chat("\n");
1328         }
1329         for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1330                 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1331                 first = 0;
1332                 ndp->offset += 32;
1333                 if(ndp->offset == bp->sectsize){
1334                         chat("long name moving over sector boundary\n");
1335                         ndp->p->flags |= BMOD;
1336                         putsect(ndp->p);
1337                         ndp->p = nil;
1338
1339                         /*
1340                          * switch to the next cluster for a long entry
1341                          * naddr should be set up correctly by searchdir
1342                          */
1343                         ndp->prevaddr = ndp->addr;
1344                         ndp->addr = ndp->naddr;
1345                         ndp->naddr = -1;
1346                         if(ndp->addr == -1)
1347                                 return -1;
1348                         ndp->p = getsect(xf, ndp->addr);
1349                         if(ndp->p == nil)
1350                                 return -1;
1351                         ndp->offset = 0;
1352                         ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1353                 }
1354         }
1355         return 0;
1356 }
1357
1358 long
1359 getfat(Xfs *xf, long n)
1360 {
1361         Dosbpb *bp = xf->ptr;
1362         Iosect *p;
1363         ulong k, o;
1364         vlong sect;
1365         int fb;
1366
1367         if(n < FATRESRV || n >= bp->fatclusters)
1368                 return -1;
1369         fb = bp->fatbits;
1370         k = (fb * n) >> 3;
1371         if(k >= bp->fatsize*bp->sectsize)
1372                 panic("getfat");
1373         sect = k/bp->sectsize + bp->fataddr;
1374         o = k%bp->sectsize;
1375         p = getsect(xf, sect);
1376         if(p == nil)
1377                 return -1;
1378         k = p->iobuf[o++];
1379         if(o >= bp->sectsize){
1380                 putsect(p);
1381                 p = getsect(xf, sect+1);
1382                 if(p == nil)
1383                         return -1;
1384                 o = 0;
1385         }
1386         k |= p->iobuf[o++]<<8;
1387         if(fb == 32){
1388                 /* fat32 is really fat28 */
1389                 k |= p->iobuf[o++] << 16;
1390                 k |= (p->iobuf[o] & 0x0f) << 24;
1391                 fb = 28;
1392         }
1393         putsect(p);
1394         if(fb == 12){
1395                 if(n&1)
1396                         k >>= 4;
1397                 else
1398                         k &= 0xfff;
1399         }
1400         if(chatty > 1)
1401                 chat("fat(%#lx)=%#lux...", n, k);
1402
1403         /*
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.
1408          */
1409         if(k >= (1UL << fb) - 8)
1410                 return -1;
1411
1412         return k;
1413 }
1414
1415 void
1416 putfat(Xfs *xf, int n, ulong val)
1417 {
1418         Fatinfo *fi;
1419         Dosbpb *bp;
1420         Iosect *p;
1421         ulong k;
1422         vlong sect, esect;
1423         int o;
1424
1425         bp = xf->ptr;
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)
1430                 panic("putfat");
1431         sect = k/bp->sectsize + bp->fataddr;
1432         esect = sect + bp->nfats * bp->fatsize;
1433         for(; sect<esect; sect+=bp->fatsize){
1434                 o = k%bp->sectsize;
1435                 p = getsect(xf, sect);
1436                 if(p == nil)
1437                         continue;
1438                 switch(bp->fatbits){
1439                 case 12:
1440                         if(n&1){
1441                                 p->iobuf[o] &= 0x0f;
1442                                 p->iobuf[o++] |= val<<4;
1443                                 if(o >= bp->sectsize){
1444                                         p->flags |= BMOD;
1445                                         putsect(p);
1446                                         p = getsect(xf, sect+1);
1447                                         if(p == nil)
1448                                                 continue;
1449                                         o = 0;
1450                                 }
1451                                 p->iobuf[o] = val>>4;
1452                         }else{
1453                                 p->iobuf[o++] = val;
1454                                 if(o >= bp->sectsize){
1455                                         p->flags |= BMOD;
1456                                         putsect(p);
1457                                         p = getsect(xf, sect+1);
1458                                         if(p == nil)
1459                                                 continue;
1460                                         o = 0;
1461                                 }
1462                                 p->iobuf[o] &= 0xf0;
1463                                 p->iobuf[o] |= (val>>8) & 0x0f;
1464                         }
1465                         break;
1466                 case 16:
1467                         p->iobuf[o++] = val;
1468                         p->iobuf[o] = val>>8;
1469                         break;
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);
1475                         break;
1476                 default:
1477                         panic("putfat fatbits");
1478                 }
1479                 p->flags |= BMOD;
1480                 putsect(p);
1481         }
1482
1483         if(val == 0)
1484                 bp->freeclusters++;
1485         else
1486                 bp->freeclusters--;
1487
1488         if(bp->fatinfo){
1489                 p = getsect(xf, bp->fatinfo);
1490                 if(p != nil){
1491                         fi = (Fatinfo*)p->iobuf;
1492                         PLONG(fi->nextfree, bp->freeptr);
1493                         PLONG(fi->freeclust, bp->freeclusters);
1494                         p->flags |= BMOD;
1495                         putsect(p);
1496                 }
1497         }
1498 }
1499
1500 /*
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.
1505  */
1506 int
1507 cfalloc(Xfile *f)
1508 {
1509         int l;
1510
1511         if((l=makecontig(f, 8)) >= 0)
1512                 return l;
1513         return makecontig(f, 1);
1514 }
1515
1516 /*
1517  * Check whether a file is contiguous.
1518  */
1519 int
1520 iscontig(Xfs *xf, Dosdir *d)
1521 {
1522         long clust, next;
1523
1524         clust = getstart(xf, d);
1525         if(clust <= 0)
1526                 return 1;
1527
1528         for(;;) {
1529                 next = getfat(xf, clust);
1530                 if(next < 0)
1531                         return 1;
1532                 if(next != clust+1)
1533                         return 0;
1534                 clust = next;
1535         }
1536 }
1537
1538 /*
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.
1542  */
1543 int
1544 makecontig(Xfile *f, int nextra)
1545 {
1546         Dosbpb *bp;
1547         Dosdir *d;
1548         Dosptr *dp;
1549         Xfs *xf;
1550         Iosect *wp, *rp;
1551         long clust, next, last, start, rclust, wclust, eclust, ostart;
1552         int isok, i, n, nclust, nrun;
1553         vlong rs, ws;
1554
1555         xf = f->xf;
1556         bp = xf->ptr;
1557         dp = f->ptr;
1558         d = dp->d;
1559
1560         isok = 1;
1561         nclust = 0;
1562         clust = fileclust(f, 0, 0);
1563         chat("clust %#lux", clust);
1564         if(clust != -1) {
1565                 for(;;) {
1566                         nclust++;
1567                         chat(".");
1568                         next = getfat(xf, clust);
1569                         if(next <= 0)
1570                                 break;
1571                         if(next != clust+1)
1572                                 isok = 0;
1573                         clust = next;
1574                 }
1575         }
1576         chat("nclust %d\n", nclust);
1577
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)
1583                                 break;
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 */
1590                         return eclust;
1591                 }
1592         }
1593
1594         /* need to search for nclust+nextra contiguous free blocks */
1595         last = -1;
1596         n = bp->freeptr;
1597         nrun = 0;
1598         for(;;){
1599                 if(getfat(xf, n) == 0) {
1600                         if(last+1 == n)
1601                                 nrun++;
1602                         else
1603                                 nrun = 1;
1604                         if(nrun >= nclust+nextra)
1605                                 break;
1606                         last = n;
1607                 }
1608                 if(++n >= bp->fatclusters)
1609                         n = FATRESRV;
1610                 if(n == bp->freeptr) {
1611                         errno = Econtig;
1612                         return -1;
1613                 }
1614         }
1615         bp->freeptr = n+1;
1616
1617         /* copy old data over */
1618         start = n+1 - nrun;
1619
1620         /* sanity check */
1621         for(i=0; i<nclust+nextra; i++)
1622                 assert(getfat(xf, start+i) == 0);
1623
1624         chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1625
1626         wclust = start;
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);
1632                         if(rp == nil)
1633                                 return -1;
1634                         wp = getosect(xf, ws);
1635                         assert(wp != nil);
1636                         memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1637                         wp->flags = BMOD;
1638                         putsect(rp);
1639                         putsect(wp);
1640                 }
1641                 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1642                 next = getfat(xf, rclust);
1643                 putfat(xf, wclust, wclust+1);
1644                 wclust++;
1645         }
1646
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 */
1651
1652         /* update directory entry to point at new start */
1653         ostart = fileclust(f, 0, 0);
1654         putstart(xf, d, start);
1655
1656         /* check our work */
1657         i = 0;
1658         clust = fileclust(f, 0, 0);
1659         if(clust != -1) {
1660                 for(;;) {
1661                         i++;
1662                         next = getfat(xf, clust);
1663                         if(next <= 0)
1664                                 break;
1665                         assert(next == clust+1);
1666                         clust = next;
1667                 }
1668         }
1669         chat("chain check: len %d\n", i);
1670         assert(i == nclust+1);
1671
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 */
1676         }
1677
1678         return start+nclust;
1679 }       
1680
1681 int
1682 falloc(Xfs *xf)
1683 {
1684         Dosbpb *bp = xf->ptr;
1685         Iosect *p;
1686         int n, i, k;
1687
1688         n = bp->freeptr;
1689         for(;;){
1690                 if(getfat(xf, n) == 0)
1691                         break;
1692                 if(++n >= bp->fatclusters)
1693                         n = FATRESRV;
1694                 if(n == bp->freeptr)
1695                         return -1;
1696         }
1697         bp->freeptr = n+1;
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);
1705                 p->flags = BMOD;
1706                 putsect(p);
1707         }
1708         return n;
1709 }
1710
1711 void
1712 ffree(Xfs *xf, long start)
1713 {
1714         putfat(xf, start, 0);
1715 }
1716
1717 vlong
1718 clust2sect(Dosbpb *bp, long clust)
1719 {
1720         return bp->dataaddr + ((vlong)(clust - FATRESRV) * bp->clustsize);
1721 }
1722
1723 long
1724 sect2clust(Dosbpb *bp, vlong sect)
1725 {
1726         return ((sect - bp->dataaddr) / bp->clustsize) + FATRESRV;
1727 }
1728
1729 void
1730 puttime(Dosdir *d, long s)
1731 {
1732         Tm *t;
1733         ushort x;
1734
1735         if(s == 0)
1736                 s = time(0);
1737         t = localtime(s);
1738
1739         x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1740         PSHORT(d->time, x);
1741         x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1742         PSHORT(d->date, x);
1743         PSHORT(d->adate, x);
1744 }
1745
1746 long
1747 gtime(Dosdir *dp)
1748 {
1749         Tm tm;
1750         int i;
1751
1752         i = GSHORT(dp->time);
1753
1754         memset(&tm, 0, sizeof(tm));
1755         tm.hour = i >> 11;
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;
1761         tm.mday = i & 31;
1762         tm.zone[0] = '\0';
1763         tm.tzoff = 0;
1764         tm.yday = 0;
1765
1766         return tm2sec(&tm);
1767 }
1768
1769 /*
1770  * structure dumps for debugging
1771  */
1772 void
1773 bootdump(int fd, Dosboot *b)
1774 {
1775         Biobuf bp;
1776
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);
1798         Bterm(&bp);
1799 }
1800
1801 void
1802 bootdump32(int fd, Dosboot32 *b)
1803 {
1804         Biobuf bp;
1805
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]);
1832         Bterm(&bp);
1833 }
1834
1835 void
1836 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1837 {
1838         Fatinfo *fi;
1839         Iosect *p1;
1840         int fisec, bsec, res;
1841
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){
1847                 if(bsec >= res)
1848                         fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1849                 else {
1850                         p1 = getsect(xf, bsec);
1851                         if(p1 == nil)
1852                                 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1853                         else{
1854                                 fprint(fd, "\nbackup boot\n");
1855                                 bootdump32(fd, (Dosboot32*)p1->iobuf);
1856                                 putsect(p1);
1857                         }
1858                 }
1859         }
1860         fisec = GSHORT(b32->infospec);
1861         if(fisec != 0 && fisec != 0xffff){
1862                 if(fisec >= res)
1863                         fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res);
1864                 else {
1865                         p1 = getsect(xf, fisec);
1866                         if(p1 == nil)
1867                                 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1868                         else{
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));
1876                                 putsect(p1);
1877                         }
1878                 }
1879         }
1880 }
1881
1882 void
1883 dirdump(void *vdbuf)
1884 {
1885         static char attrchar[] = "rhsvda67";
1886         Dosdir *d;
1887         char *name, namebuf[DOSNAMELEN];
1888         char buf[128], *s, *ebuf;
1889         uchar *dbuf;
1890         int i;
1891
1892         if(!chatty)
1893                 return;
1894
1895         d = vdbuf;
1896
1897         ebuf = buf + sizeof(buf);
1898         if((d->attr & 0xf) == 0xf){
1899                 dbuf = vdbuf;
1900                 name = namebuf + DOSNAMELEN;
1901                 *--name = '\0';
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));
1904         }else{
1905                 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1906                 for(i=7; i>=0; i--)
1907                         *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1908         
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);
1913         
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);
1918         
1919                 i = GSHORT(d->adate);
1920                 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1921
1922                 seprint(s, ebuf, " %d %lud", GSHORT(d->start), GLONG(d->length));
1923         }
1924         chat("%s\n", buf);
1925 }
1926
1927 int
1928 cistrcmp(char *s1, char *s2)
1929 {
1930         int c1, c2;
1931
1932         while(*s1){
1933                 c1 = *s1++;
1934                 c2 = *s2++;
1935
1936                 if(c1 >= 'A' && c1 <= 'Z')
1937                         c1 -= 'A' - 'a';
1938
1939                 if(c2 >= 'A' && c2 <= 'Z')
1940                         c2 -= 'A' - 'a';
1941
1942                 if(c1 != c2)
1943                         return c1 - c2;
1944         }
1945         return -*s2;
1946 }
1947
1948 int
1949 utftorunes(Rune *rr, char *s, int n)
1950 {
1951         Rune *r, *re;
1952         int c;
1953
1954         r = rr;
1955         re = r + n - 1;
1956         while(c = (uchar)*s){
1957                 if(c < Runeself){
1958                         *r = c;
1959                         s++;
1960                 }else
1961                         s += chartorune(r, s);
1962                 r++;
1963                 if(r >= re)
1964                         break;
1965         }
1966         *r = 0;
1967         return r - rr;
1968 }