]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/dossrv/dossubs.c
dossrv: handle file offsets > 2^31
[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 == 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, vlong 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, vlong 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         ulong length, rcnt;
934
935         if(offset >= MAXFILELEN)
936                 return 0;
937         if(offset+count > MAXFILELEN)
938                 count = MAXFILELEN - offset;
939
940         rcnt = 0;
941         length = GLONG(d->length);
942         buf = vbuf;
943         if(offset >= length)
944                 return 0;
945         if(offset+count >= length)
946                 count = length - offset;
947         isect = offset/bp->sectsize;
948         o = offset%bp->sectsize;
949         while(count > 0){
950                 addr = fileaddr(f, isect++, 0);
951                 if(addr < 0)
952                         break;
953                 c = bp->sectsize - o;
954                 if(c > count)
955                         c = count;
956                 p = getsect(xf, addr);
957                 if(p == 0)
958                         return -1;
959                 memmove(&buf[rcnt], &p->iobuf[o], c);
960                 putsect(p);
961                 count -= c;
962                 rcnt += c;
963                 o = 0;
964         }
965         return rcnt;
966 }
967
968 long
969 writefile(Xfile *f, void *vbuf, vlong offset, long count)
970 {
971         Xfs *xf = f->xf;
972         Dosbpb *bp = xf->ptr;
973         Dosptr *dp = f->ptr;
974         Dosdir *d = dp->d;
975         int isect, addr, o, c;
976         Iosect *p;
977         uchar *buf;
978         ulong length, rcnt, dlen;
979
980         if(offset >= MAXFILELEN)
981                 return 0;
982         if(offset+count > MAXFILELEN)
983                 count = MAXFILELEN - offset;
984
985         rcnt = 0;
986         addr = 0;
987         buf = vbuf;
988         isect = offset/bp->sectsize;
989         o = offset%bp->sectsize;
990         while(count > 0){
991                 addr = fileaddr(f, isect++, 1);
992                 if(addr < 0)
993                         break;
994                 c = bp->sectsize - o;
995                 if(c > count)
996                         c = count;
997                 if(c == bp->sectsize){
998                         p = getosect(xf, addr);
999                         p->flags = 0;
1000                 }else{
1001                         p = getsect(xf, addr);
1002                         if(p == nil)
1003                                 return -1;
1004                 }
1005                 memmove(&p->iobuf[o], &buf[rcnt], c);
1006                 p->flags |= BMOD;
1007                 putsect(p);
1008                 count -= c;
1009                 rcnt += c;
1010                 o = 0;
1011         }
1012         if(addr < 0)
1013                 return -1;
1014         length = 0;
1015         dlen = GLONG(d->length);
1016         if(rcnt > 0)
1017                 length = offset+rcnt;
1018         else if(dp->addr && dp->clust){
1019                 c = bp->clustsize*bp->sectsize;
1020                 if(dp->iclust > (dlen+c-1)/c)
1021                         length = c*dp->iclust;
1022         }
1023         if(length > dlen)
1024                 PLONG(d->length, length);
1025         puttime(d, 0);
1026         dp->p->flags |= BMOD;
1027         return rcnt;
1028 }
1029
1030 int
1031 truncfile(Xfile *f, vlong length)
1032 {
1033         Xfs *xf = f->xf;
1034         Dosbpb *bp = xf->ptr;
1035         Dosptr *dp = f->ptr;
1036         Dosdir *d = dp->d;
1037         long clust, next;
1038         vlong n;
1039
1040         if(length > MAXFILELEN)
1041                 return -1;
1042
1043         mlock(bp);
1044         clust = getstart(f->xf, d);
1045         n = length;
1046         if(n <= 0)
1047                 putstart(f->xf, d, 0);
1048         else
1049                 n -= bp->sectsize;
1050         while(clust > 0){
1051                 next = getfat(xf, clust);
1052                 if(n <= 0)
1053                         putfat(xf, clust, 0);
1054                 else
1055                         n -= bp->clustsize*bp->sectsize;
1056                 clust = next;
1057         }
1058         unmlock(bp);
1059         PLONG(d->length, length);
1060         dp->iclust = 0;
1061         dp->clust = 0;
1062         dp->p->flags |= BMOD;
1063         return 0;
1064 }
1065
1066 void
1067 putdir(Dosdir *d, Dir *dp)
1068 {
1069         if(dp->mode != ~0){
1070                 if(dp->mode & 2)
1071                         d->attr &= ~DRONLY;
1072                 else
1073                         d->attr |= DRONLY;
1074                 if(dp->mode & DMEXCL)
1075                         d->attr |= DSYSTEM;
1076                 else
1077                         d->attr &= ~DSYSTEM;
1078         }
1079         if(dp->mtime != ~0)
1080                 puttime(d, dp->mtime);
1081 }
1082
1083 /*
1084  * should extend this to deal with
1085  * creation and access dates
1086  */
1087 void
1088 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset)
1089 {
1090         if(d == nil || addr == 0)
1091                 panic("getdir on root");
1092         dp->type = 0;
1093         dp->dev = 0;
1094         getname(dp->name, d);
1095
1096         dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1097                         offset/DOSDIRSIZE;
1098         dp->qid.vers = 0;
1099
1100         if(d->attr & DRONLY)
1101                 dp->mode = 0444;
1102         else
1103                 dp->mode = 0666;
1104         dp->atime = gtime(d);
1105         dp->mtime = dp->atime;
1106         dp->qid.type = QTFILE;
1107         if(d->attr & DDIR){
1108                 dp->qid.type = QTDIR;
1109                 dp->mode |= DMDIR|0111;
1110                 dp->length = 0;
1111         }else
1112                 dp->length = (ulong)GLONG(d->length);
1113         if(d->attr & DSYSTEM){
1114                 dp->mode |= DMEXCL;
1115                 if(iscontig(xfs, d))
1116                         dp->mode |= DMAPPEND;
1117         }
1118
1119         dp->uid = "bill";
1120         dp->muid = "bill";
1121         dp->gid = "trog";
1122 }
1123
1124 void
1125 getname(char *p, Dosdir *d)
1126 {
1127         int c, i;
1128
1129         for(i=0; i<8; i++){
1130                 c = d->name[i];
1131                 if(c == '\0' || c == ' ')
1132                         break;
1133                 if(i == 0 && c == 0x05)
1134                         c = 0xe5;
1135                 *p++ = tolower(c);
1136         }
1137         for(i=0; i<3; i++){
1138                 c = d->ext[i];
1139                 if(c == '\0' || c == ' ')
1140                         break;
1141                 if(i == 0)
1142                         *p++ = '.';
1143                 *p++ = tolower(c);
1144         }
1145         *p = 0;
1146 }
1147
1148 static char*
1149 getnamerunes(char *dst, uchar *buf, int step)
1150 {
1151         int i;
1152         Rune r;
1153         char dbuf[DOSRUNE * UTFmax + 1], *d;
1154
1155         d = dbuf;
1156         r = 1;
1157         for(i = 1; r && i < 11; i += 2){
1158                 r = buf[i] | (buf[i+1] << 8);
1159                 d += runetochar(d, &r);
1160         }
1161         for(i = 14; r && i < 26; i += 2){
1162                 r = buf[i] | (buf[i+1] << 8);
1163                 d += runetochar(d, &r);
1164         }
1165         for(i = 28; r && i < 32; i += 2){
1166                 r = buf[i] | (buf[i+1] << 8);
1167                 d += runetochar(d, &r);
1168         }
1169
1170         if(step == 1)
1171                 dst -= d - dbuf;
1172
1173         memmove(dst, dbuf, d - dbuf);
1174
1175         if(step == -1){
1176                 dst += d - dbuf;
1177                 *dst = '\0';
1178         }
1179
1180         return dst;
1181 }
1182
1183 char*
1184 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1185 {
1186         /*
1187          * validation checks to make sure we're
1188          * making up a consistent name
1189          */
1190         if(buf[11] != 0xf || buf[12] != 0){
1191                 *islong = 0;
1192                 return nil;
1193         }
1194         if(step == 1){
1195                 if((buf[0] & 0xc0) == 0x40){
1196                         *islong = buf[0] & 0x3f;
1197                         *sum = buf[13];
1198                         d = dbuf + DOSNAMELEN;
1199                         *--d = '\0';
1200                 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1201                         *islong = buf[0];
1202                 }else{
1203                         *islong = 0;
1204                         return nil;
1205                 }
1206         }else{
1207                 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1208                         *islong = buf[0] & 0x3f;
1209                         if(buf[0] & 0x40)
1210                                 *sum = -1;
1211                 }else{
1212                         *islong = 0;
1213                         *sum = -1;
1214                         return nil;
1215                 }
1216         }
1217         if(*islong > 20){
1218                 *islong = 0;
1219                 *sum = -1;
1220                 return nil;
1221         }
1222
1223         return getnamerunes(d, buf, step);
1224 }
1225
1226 void
1227 putname(char *p, Dosdir *d)
1228 {
1229         int i;
1230
1231         memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1232         for(i=0; i<sizeof d->name; i++){
1233                 if(*p == 0 || *p == '.')
1234                         break;
1235                 d->name[i] = toupper(*p++);
1236         }
1237         p = strrchr(p, '.');
1238         if(p){
1239                 for(i=0; i<sizeof d->ext; i++){
1240                         if(*++p == 0)
1241                                 break;
1242                         d->ext[i] = toupper(*p);
1243                 }
1244         }
1245 }
1246
1247 static void
1248 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1249 {
1250         Rune r;
1251         Dosdir ds;
1252         int i, j;
1253
1254         memset(&ds, 0xff, sizeof ds);
1255         ds.attr = 0xf;
1256         ds.reserved[0] = 0;
1257         ds.reserved[1] = sum;
1258         ds.start[0] = 0;
1259         ds.start[1] = 0;
1260         if(first)
1261                 ds.name[0] = 0x40 | curslot;
1262         else 
1263                 ds.name[0] = curslot;
1264         memmove(slot, &ds, sizeof ds);
1265
1266         j = (curslot-1) * DOSRUNE;
1267
1268         for(i = 1; i < 11; i += 2){
1269                 r = longname[j++];
1270                 slot[i] = r;
1271                 slot[i+1] = r >> 8;
1272                 if(r == 0)
1273                         return;
1274         }
1275         for(i = 14; i < 26; i += 2){
1276                 r = longname[j++];
1277                 slot[i] = r;
1278                 slot[i+1] = r >> 8;
1279                 if(r == 0)
1280                         return;
1281         }
1282         for(i = 28; i < 32; i += 2){
1283                 r = longname[j++];
1284                 slot[i] = r;
1285                 slot[i+1] = r >> 8;
1286                 if(r == 0)
1287                         return;
1288         }
1289 }
1290
1291 int
1292 aliassum(Dosdir *d)
1293 {
1294         int i, sum;
1295
1296         if(d == nil)
1297                 return -1;
1298         sum = 0;
1299         for(i = 0; i < 11; i++)
1300                 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1301         return sum & 0xff;
1302 }
1303
1304 int
1305 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1306 {
1307         Dosbpb *bp;
1308         Dosdir tmpd;
1309         Rune longname[DOSNAMELEN+1];
1310         int i, first, sum, nds, len;
1311
1312         /* calculate checksum */
1313         putname(sname, &tmpd);
1314         sum = aliassum(&tmpd);
1315
1316         bp = xf->ptr;
1317         first = 1;
1318         len = utftorunes(longname, name, DOSNAMELEN);
1319         if(chatty){
1320                 chat("utftorunes %s =", name);
1321                 for(i=0; i<len; i++)
1322                         chat(" %.4X", longname[i]);
1323                 chat("\n");
1324         }
1325         for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1326                 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1327                 first = 0;
1328                 ndp->offset += 32;
1329                 if(ndp->offset == bp->sectsize){
1330                         chat("long name moving over sector boundary\n");
1331                         ndp->p->flags |= BMOD;
1332                         putsect(ndp->p);
1333                         ndp->p = nil;
1334
1335                         /*
1336                          * switch to the next cluster for a long entry
1337                          * naddr should be set up correctly by searchdir
1338                          */
1339                         ndp->prevaddr = ndp->addr;
1340                         ndp->addr = ndp->naddr;
1341                         ndp->naddr = -1;
1342                         if(ndp->addr == -1)
1343                                 return -1;
1344                         ndp->p = getsect(xf, ndp->addr);
1345                         if(ndp->p == nil)
1346                                 return -1;
1347                         ndp->offset = 0;
1348                         ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1349                 }
1350         }
1351         return 0;
1352 }
1353
1354 long
1355 getfat(Xfs *xf, int n)
1356 {
1357         Dosbpb *bp = xf->ptr;
1358         Iosect *p;
1359         ulong k, sect;
1360         int o, fb;
1361
1362         if(n < FATRESRV || n >= bp->fatclusters)
1363                 return -1;
1364         fb = bp->fatbits;
1365         k = (fb * n) >> 3;
1366         if(k >= bp->fatsize*bp->sectsize)
1367                 panic("getfat");
1368         sect = k/bp->sectsize + bp->fataddr;
1369         o = k%bp->sectsize;
1370         p = getsect(xf, sect);
1371         if(p == nil)
1372                 return -1;
1373         k = p->iobuf[o++];
1374         if(o >= bp->sectsize){
1375                 putsect(p);
1376                 p = getsect(xf, sect+1);
1377                 if(p == nil)
1378                         return -1;
1379                 o = 0;
1380         }
1381         k |= p->iobuf[o++]<<8;
1382         if(fb == 32){
1383                 /* fat32 is really fat28 */
1384                 k |= p->iobuf[o++] << 16;
1385                 k |= (p->iobuf[o] & 0x0f) << 24;
1386                 fb = 28;
1387         }
1388         putsect(p);
1389         if(fb == 12){
1390                 if(n&1)
1391                         k >>= 4;
1392                 else
1393                         k &= 0xfff;
1394         }
1395         if(chatty > 1)
1396                 chat("fat(%#x)=%#lx...", n, k);
1397
1398         /*
1399          * This is a very strange check for out of range.
1400          * As a concrete example, for a 16-bit FAT,
1401          * FFF8 through FFFF all signify ``end of cluster chain.''
1402          * This generalizes to other-sized FATs.
1403          */
1404         if(k >= (1 << fb) - 8)
1405                 return -1;
1406
1407         return k;
1408 }
1409
1410 void
1411 putfat(Xfs *xf, int n, ulong val)
1412 {
1413         Fatinfo *fi;
1414         Dosbpb *bp;
1415         Iosect *p;
1416         ulong k, sect, esect;
1417         int o;
1418
1419         bp = xf->ptr;
1420         if(n < FATRESRV || n >= bp->fatclusters)
1421                 panic("putfat n=%d", n);
1422         k = (bp->fatbits * n) >> 3;
1423         if(k >= bp->fatsize*bp->sectsize)
1424                 panic("putfat");
1425         sect = k/bp->sectsize + bp->fataddr;
1426         esect = sect + bp->nfats * bp->fatsize;
1427         for(; sect<esect; sect+=bp->fatsize){
1428                 o = k%bp->sectsize;
1429                 p = getsect(xf, sect);
1430                 if(p == nil)
1431                         continue;
1432                 switch(bp->fatbits){
1433                 case 12:
1434                         if(n&1){
1435                                 p->iobuf[o] &= 0x0f;
1436                                 p->iobuf[o++] |= val<<4;
1437                                 if(o >= bp->sectsize){
1438                                         p->flags |= BMOD;
1439                                         putsect(p);
1440                                         p = getsect(xf, sect+1);
1441                                         if(p == nil)
1442                                                 continue;
1443                                         o = 0;
1444                                 }
1445                                 p->iobuf[o] = val>>4;
1446                         }else{
1447                                 p->iobuf[o++] = val;
1448                                 if(o >= bp->sectsize){
1449                                         p->flags |= BMOD;
1450                                         putsect(p);
1451                                         p = getsect(xf, sect+1);
1452                                         if(p == nil)
1453                                                 continue;
1454                                         o = 0;
1455                                 }
1456                                 p->iobuf[o] &= 0xf0;
1457                                 p->iobuf[o] |= (val>>8) & 0x0f;
1458                         }
1459                         break;
1460                 case 16:
1461                         p->iobuf[o++] = val;
1462                         p->iobuf[o] = val>>8;
1463                         break;
1464                 case 32:        /* fat32 is really fat28 */
1465                         p->iobuf[o++] = val;
1466                         p->iobuf[o++] = val>>8;
1467                         p->iobuf[o++] = val>>16;
1468                         p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f);
1469                         break;
1470                 default:
1471                         panic("putfat fatbits");
1472                 }
1473                 p->flags |= BMOD;
1474                 putsect(p);
1475         }
1476
1477         if(val == 0)
1478                 bp->freeclusters++;
1479         else
1480                 bp->freeclusters--;
1481
1482         if(bp->fatinfo){
1483                 p = getsect(xf, bp->fatinfo);
1484                 if(p != nil){
1485                         fi = (Fatinfo*)p->iobuf;
1486                         PLONG(fi->nextfree, bp->freeptr);
1487                         PLONG(fi->freeclust, bp->freeclusters);
1488                         p->flags |= BMOD;
1489                         putsect(p);
1490                 }
1491         }
1492 }
1493
1494 /*
1495  * Contiguous falloc; if we can, use lastclust+1.
1496  * Otherwise, move the file to get some space.
1497  * If there just isn't enough contiguous space
1498  * anywhere on disk, fail.
1499  */
1500 int
1501 cfalloc(Xfile *f)
1502 {
1503         int l;
1504
1505         if((l=makecontig(f, 8)) >= 0)
1506                 return l;
1507         return makecontig(f, 1);
1508 }
1509
1510 /*
1511  * Check whether a file is contiguous.
1512  */
1513 int
1514 iscontig(Xfs *xf, Dosdir *d)
1515 {
1516         long clust, next;
1517
1518         clust = getstart(xf, d);
1519         if(clust <= 0)
1520                 return 1;
1521
1522         for(;;) {
1523                 next = getfat(xf, clust);
1524                 if(next < 0)
1525                         return 1;
1526                 if(next != clust+1)
1527                         return 0;
1528                 clust = next;
1529         }
1530 }
1531
1532 /*
1533  * Make a file contiguous, with nextra clusters of 
1534  * free space after it for later expansion.
1535  * Return the number of the first new cluster.
1536  */
1537 int
1538 makecontig(Xfile *f, int nextra)
1539 {
1540         Dosbpb *bp;
1541         Dosdir *d;
1542         Dosptr *dp;
1543         Xfs *xf;
1544         Iosect *wp, *rp;
1545         long clust, next, last, start, rclust, wclust, eclust, ostart;
1546         int isok, i, n, nclust, nrun, rs, ws;
1547
1548         xf = f->xf;
1549         bp = xf->ptr;
1550         dp = f->ptr;
1551         d = dp->d;
1552
1553         isok = 1;
1554         nclust = 0;
1555         clust = fileclust(f, 0, 0);
1556         chat("clust %#lux", clust);
1557         if(clust != -1) {
1558                 for(;;) {
1559                         nclust++;
1560                         chat(".");
1561                         next = getfat(xf, clust);
1562                         if(next <= 0)
1563                                 break;
1564                         if(next != clust+1)
1565                                 isok = 0;
1566                         clust = next;
1567                 }
1568         }
1569         chat("nclust %d\n", nclust);
1570
1571         if(isok && clust != -1) {
1572                 eclust = clust+1;       /* eclust = first cluster past file */
1573                 assert(eclust == fileclust(f, 0, 0)+nclust);
1574                 for(i=0; i<nextra; i++)
1575                         if(getfat(xf, eclust+i) != 0)
1576                                 break;
1577                 if(i == nextra) {       /* they were all free */
1578                         chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1));
1579                         assert(getfat(xf, eclust-1) == 0xffffffff);
1580                         putfat(xf, eclust-1, eclust);
1581                         putfat(xf, eclust, 0xffffffff);
1582                         bp->freeptr = clust+1;  /* to help keep the blocks free */
1583                         return eclust;
1584                 }
1585         }
1586
1587         /* need to search for nclust+nextra contiguous free blocks */
1588         last = -1;
1589         n = bp->freeptr;
1590         nrun = 0;
1591         for(;;){
1592                 if(getfat(xf, n) == 0) {
1593                         if(last+1 == n)
1594                                 nrun++;
1595                         else
1596                                 nrun = 1;
1597                         if(nrun >= nclust+nextra)
1598                                 break;
1599                         last = n;
1600                 }
1601                 if(++n >= bp->fatclusters)
1602                         n = FATRESRV;
1603                 if(n == bp->freeptr) {
1604                         errno = Econtig;
1605                         return -1;
1606                 }
1607         }
1608         bp->freeptr = n+1;
1609
1610         /* copy old data over */
1611         start = n+1 - nrun;
1612
1613         /* sanity check */
1614         for(i=0; i<nclust+nextra; i++)
1615                 assert(getfat(xf, start+i) == 0);
1616
1617         chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1618
1619         wclust = start;
1620         for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){
1621                 rs = clust2sect(bp, rclust);
1622                 ws = clust2sect(bp, wclust);
1623                 for(i=0; i<bp->clustsize; i++, rs++, ws++){
1624                         rp = getsect(xf, rs);
1625                         if(rp == nil)
1626                                 return -1;
1627                         wp = getosect(xf, ws);
1628                         assert(wp != nil);
1629                         memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1630                         wp->flags = BMOD;
1631                         putsect(rp);
1632                         putsect(wp);
1633                 }
1634                 chat("move cluster %#lx -> %#lx...", rclust, wclust);
1635                 next = getfat(xf, rclust);
1636                 putfat(xf, wclust, wclust+1);
1637                 wclust++;
1638         }
1639
1640         /* now wclust points at the first new cluster; chain it in */
1641         chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust);
1642         assert(wclust == start+nclust);
1643         putfat(xf, wclust, 0xffffffff); /* end of file */
1644
1645         /* update directory entry to point at new start */
1646         ostart = fileclust(f, 0, 0);
1647         putstart(xf, d, start);
1648
1649         /* check our work */
1650         i = 0;
1651         clust = fileclust(f, 0, 0);
1652         if(clust != -1) {
1653                 for(;;) {
1654                         i++;
1655                         next = getfat(xf, clust);
1656                         if(next <= 0)
1657                                 break;
1658                         assert(next == clust+1);
1659                         clust = next;
1660                 }
1661         }
1662         chat("chain check: len %d\n", i);
1663         assert(i == nclust+1);
1664
1665         /* succeeded; remove old chain. */
1666         for(rclust = ostart; rclust > 0; rclust = next){
1667                 next = getfat(xf, rclust);
1668                 putfat(xf, rclust, 0);  /* free cluster */
1669         }
1670
1671         return start+nclust;
1672 }       
1673
1674 int
1675 falloc(Xfs *xf)
1676 {
1677         Dosbpb *bp = xf->ptr;
1678         Iosect *p;
1679         int n, i, k;
1680
1681         n = bp->freeptr;
1682         for(;;){
1683                 if(getfat(xf, n) == 0)
1684                         break;
1685                 if(++n >= bp->fatclusters)
1686                         n = FATRESRV;
1687                 if(n == bp->freeptr)
1688                         return -1;
1689         }
1690         bp->freeptr = n+1;
1691         if(bp->freeptr >= bp->fatclusters)
1692                 bp->freeptr = FATRESRV;
1693         putfat(xf, n, 0xffffffff);
1694         k = clust2sect(bp, n);
1695         for(i=0; i<bp->clustsize; i++){
1696                 p = getosect(xf, k+i);
1697                 memset(p->iobuf, 0, bp->sectsize);
1698                 p->flags = BMOD;
1699                 putsect(p);
1700         }
1701         return n;
1702 }
1703
1704 void
1705 ffree(Xfs *xf, long start)
1706 {
1707         putfat(xf, start, 0);
1708 }
1709
1710 long
1711 clust2sect(Dosbpb *bp, long clust)
1712 {
1713         return bp->dataaddr + (clust - FATRESRV) * bp->clustsize;
1714 }
1715
1716 long
1717 sect2clust(Dosbpb *bp, long sect)
1718 {
1719         long c;
1720
1721         c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV;
1722         assert(sect == clust2sect(bp, c));
1723         return c;
1724 }
1725
1726 void
1727 puttime(Dosdir *d, long s)
1728 {
1729         Tm *t;
1730         ushort x;
1731
1732         if(s == 0)
1733                 s = time(0);
1734         t = localtime(s);
1735
1736         x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1737         PSHORT(d->time, x);
1738         x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1739         PSHORT(d->date, x);
1740         PSHORT(d->adate, x);
1741 }
1742
1743 long
1744 gtime(Dosdir *dp)
1745 {
1746         Tm tm;
1747         int i;
1748
1749         i = GSHORT(dp->time);
1750         tm.hour = i >> 11;
1751         tm.min = (i >> 5) & 63;
1752         tm.sec = (i & 31) << 1;
1753         i = GSHORT(dp->date);
1754         tm.year = 80 + (i >> 9);
1755         tm.mon = ((i >> 5) & 15) - 1;
1756         tm.mday = i & 31;
1757         tm.zone[0] = '\0';
1758         tm.tzoff = 0;
1759         tm.yday = 0;
1760
1761         return tm2sec(&tm);
1762 }
1763
1764 /*
1765  * structure dumps for debugging
1766  */
1767 void
1768 bootdump(int fd, Dosboot *b)
1769 {
1770         Biobuf bp;
1771
1772         Binit(&bp, fd, OWRITE);
1773         Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1774                 b->magic[0], b->magic[1], b->magic[2]);
1775         Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1776         Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1777         Bprint(&bp, "clustsize: %d\n", b->clustsize);
1778         Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1779         Bprint(&bp, "nfats: %d\n", b->nfats);
1780         Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1781         Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1782         Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1783         Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1784         Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1785         Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1786         Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1787         Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1788         Bprint(&bp, "driveno: %d\n", b->driveno);
1789         Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0);
1790         Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig);
1791         Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid));
1792         Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label);
1793         Bterm(&bp);
1794 }
1795
1796 void
1797 bootdump32(int fd, Dosboot32 *b)
1798 {
1799         Biobuf bp;
1800
1801         Binit(&bp, fd, OWRITE);
1802         Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1803                 b->magic[0], b->magic[1], b->magic[2]);
1804         Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1805         Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1806         Bprint(&bp, "clustsize: %d\n", b->clustsize);
1807         Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1808         Bprint(&bp, "nfats: %d\n", b->nfats);
1809         Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1810         Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1811         Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1812         Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1813         Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1814         Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1815         Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1816         Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1817         Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32));
1818         Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
1819         Bprint(&bp, "version: %d\n", GSHORT(b->version1));
1820         Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart));
1821         Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec));
1822         Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot));
1823         Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n",
1824                 b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3],
1825                 b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7],
1826                 b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]);
1827         Bterm(&bp);
1828 }
1829
1830 void
1831 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1832 {
1833         Fatinfo *fi;
1834         Iosect *p1;
1835         int fisec, bsec, res;
1836
1837         fprint(fd, "\nfat32\n");
1838         bootdump32(fd, b32);
1839         res = GSHORT(b32->nresrv);
1840         bsec = GSHORT(b32->backupboot);
1841         if(bsec != 0 && bsec != 0xffff){
1842                 if(bsec >= res)
1843                         fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1844                 else {
1845                         p1 = getsect(xf, bsec);
1846                         if(p1 == nil)
1847                                 fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1848                         else{
1849                                 fprint(fd, "\nbackup boot\n");
1850                                 bootdump32(fd, (Dosboot32*)p1->iobuf);
1851                                 putsect(p1);
1852                         }
1853                 }
1854         }
1855         fisec = GSHORT(b32->infospec);
1856         if(fisec != 0 && fisec != 0xffff){
1857                 if(fisec >= res)
1858                         fprint(2, "bad fat info sector: %d reserved %d\n", fisec, res);
1859                 else {
1860                         p1 = getsect(xf, fisec);
1861                         if(p1 == nil)
1862                                 fprint(fd, "\ncouldn't get fat info sector: %r\n");
1863                         else{
1864                                 fprint(fd, "\nfat info %d\n", fisec);
1865                                 fi = (Fatinfo*)p1->iobuf;
1866                                 fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1);
1867                                 fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG);
1868                                 fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust));
1869                                 fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree));
1870                                 fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8));
1871                                 putsect(p1);
1872                         }
1873                 }
1874         }
1875 }
1876
1877 void
1878 dirdump(void *vdbuf)
1879 {
1880         static char attrchar[] = "rhsvda67";
1881         Dosdir *d;
1882         char *name, namebuf[DOSNAMELEN];
1883         char buf[128], *s, *ebuf;
1884         uchar *dbuf;
1885         int i;
1886
1887         if(!chatty)
1888                 return;
1889
1890         d = vdbuf;
1891
1892         ebuf = buf + sizeof(buf);
1893         if((d->attr & 0xf) == 0xf){
1894                 dbuf = vdbuf;
1895                 name = namebuf + DOSNAMELEN;
1896                 *--name = '\0';
1897                 name = getnamerunes(name, dbuf, 1);
1898                 seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start));
1899         }else{
1900                 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1901                 for(i=7; i>=0; i--)
1902                         *s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1903         
1904                 i = GSHORT(d->time);
1905                 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1906                 i = GSHORT(d->date);
1907                 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1908         
1909                 i = GSHORT(d->ctime);
1910                 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d+%d", i>>11, (i>>5)&63, (i&31)<<1, (int)d->ctimetenth);
1911                 i = GSHORT(d->cdate);
1912                 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1913         
1914                 i = GSHORT(d->adate);
1915                 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1916
1917                 seprint(s, ebuf, " %d %lud", GSHORT(d->start), (ulong)GLONG(d->length));
1918         }
1919         chat("%s\n", buf);
1920 }
1921
1922 int
1923 cistrcmp(char *s1, char *s2)
1924 {
1925         int c1, c2;
1926
1927         while(*s1){
1928                 c1 = *s1++;
1929                 c2 = *s2++;
1930
1931                 if(c1 >= 'A' && c1 <= 'Z')
1932                         c1 -= 'A' - 'a';
1933
1934                 if(c2 >= 'A' && c2 <= 'Z')
1935                         c2 -= 'A' - 'a';
1936
1937                 if(c1 != c2)
1938                         return c1 - c2;
1939         }
1940         return -*s2;
1941 }
1942
1943 int
1944 utftorunes(Rune *rr, char *s, int n)
1945 {
1946         Rune *r, *re;
1947         int c;
1948
1949         r = rr;
1950         re = r + n - 1;
1951         while(c = (uchar)*s){
1952                 if(c < Runeself){
1953                         *r = c;
1954                         s++;
1955                 }else
1956                         s += chartorune(r, s);
1957                 r++;
1958                 if(r >= re)
1959                         break;
1960         }
1961         *r = 0;
1962         return r - rr;
1963 }