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