1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "pool.h"
9 #include "../port/error.h"
10 #include "../port/netif.h"
11 #include "dosfs.h"
12
13 enum {
14 Dosfilemax = 8,
15 Dosextmax = 3,
16 };
17
18 /*
19 * predeclared
20 */
21 static void bootdump(Dosboot*);
22 static void setname(Dosfile*, char*);
23
24 /*
25 * debugging
26 */
27 #define chatty 0
28 #define chat if(chatty)print
29
30 /*
31 * block io buffers
32 */
33 enum
34 {
35 Nbio= 16,
36 };
37 typedef struct Clustbuf Clustbuf;
38 struct Clustbuf
39 {
40 int age;
41 long sector;
42 uchar *iobuf;
43 Dos *dos;
44 int size;
45 };
46 Clustbuf bio[Nbio];
47
48 /*
49 * get an io block from an io buffer
50 */
51 Clustbuf*
getclust(Dos * dos,long sector)52 getclust(Dos *dos, long sector)
53 {
54 Bootfs *fs;
55 Clustbuf *p, *oldest;
56 int size;
57
58 chat("getclust @ %ld\n", sector);
59
60 /*
61 * if we have it, just return it
62 */
63 for(p = bio; p < &bio[Nbio]; p++){
64 if(sector == p->sector && dos == p->dos){
65 p->age = m->ticks;
66 chat("getclust %ld in cache\n", sector);
67 return p;
68 }
69 }
70
71 /*
72 * otherwise, reuse the oldest entry
73 */
74 oldest = bio;
75 for(p = &bio[1]; p < &bio[Nbio]; p++){
76 if(p->age <= oldest->age)
77 oldest = p;
78 }
79 p = oldest;
80
81 /*
82 * make sure the buffer is big enough
83 */
84 size = dos->clustsize*dos->sectsize;
85 if(p->iobuf==0 || p->size < size)
86 p->iobuf = smalloc(size);
87 p->size = size;
88
89 /*
90 * read in the cluster
91 */
92 fs = (Bootfs*)dos; /* assume dos is embedded at start of an Bootfs */
93 chat("getclust addr %llud %p %s\n", ((sector+dos->start)*(vlong)dos->sectsize),
94 fs, fs->disk);
95 fs->devch->offset = (sector+dos->start) * (vlong)dos->sectsize;
96 if(myreadn(fs->devch, p->iobuf, size) != size){
97 chat("can't read block\n");
98 return 0;
99 }
100 USED(fs);
101 p->age = m->ticks;
102 p->dos = dos;
103 p->sector = sector;
104 chat("getclust %ld read\n", sector);
105 return p;
106 }
107
108 /*
109 * walk the fat one level ( n is a current cluster number ).
110 * return the new cluster number or -1 if no more.
111 */
112 static long
fatwalk(Dos * dos,int n)113 fatwalk(Dos *dos, int n)
114 {
115 ulong k, sect;
116 Clustbuf *p;
117 int o;
118
119 chat("fatwalk %d\n", n);
120
121 if(n < 2 || n >= dos->fatclusters)
122 return -1;
123
124 switch(dos->fatbits){
125 case 12:
126 k = (3*n)/2; break;
127 case 16:
128 k = 2*n; break;
129 case 32:
130 k = 4*n; break;
131 default:
132 return -1;
133 }
134 if(k >= dos->fatsize*dos->sectsize)
135 panic("getfat");
136
137 if (dos->sectsize == 0 || dos->clustsize == 0)
138 panic("fatwalk: zero sector or cluster size");
139 sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
140 o = k%(dos->sectsize*dos->clustsize);
141 p = getclust(dos, sect);
142 k = p->iobuf[o++];
143 if(o >= dos->sectsize*dos->clustsize){
144 p = getclust(dos, sect+dos->clustsize);
145 o = 0;
146 }
147 k |= p->iobuf[o++]<<8;
148 if(dos->fatbits == 12){
149 if(n&1)
150 k >>= 4;
151 else
152 k &= 0xfff;
153 if(k >= 0xff8)
154 k = -1;
155 }
156 else if (dos->fatbits == 32){
157 if(o >= dos->sectsize*dos->clustsize){
158 p = getclust(dos, sect+dos->clustsize);
159 o = 0;
160 }
161 k |= p->iobuf[o++]<<16;
162 k |= p->iobuf[o]<<24;
163 if (k >= 0xfffffff8)
164 k = -1;
165 }
166 else
167 k = k < 0xfff8 ? k : -1;
168 chat("fatwalk %d -> %lud\n", n, k);
169 return k;
170 }
171
172 /*
173 * map a file's logical cluster address to a physical sector address
174 */
175 static long
fileaddr(Dosfile * fp,long ltarget)176 fileaddr(Dosfile *fp, long ltarget)
177 {
178 Dos *dos = fp->dos;
179 long l;
180 long p;
181
182 chat("fileaddr %8.8s %ld\n", fp->name, ltarget);
183 /*
184 * root directory is contiguous and easy (unless FAT32)
185 */
186 if(fp->pstart == 0 && dos->rootsize != 0) {
187 if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
188 return -1;
189 l = dos->rootaddr + ltarget*dos->clustsize;
190 chat("fileaddr %ld -> %ld\n", ltarget, l);
191 return l;
192 }
193
194 /*
195 * anything else requires a walk through the fat
196 */
197 if(ltarget >= fp->lcurrent && fp->pcurrent){
198 /* start at the currrent point */
199 l = fp->lcurrent;
200 p = fp->pcurrent;
201 } else {
202 /* go back to the beginning */
203 l = 0;
204 p = fp->pstart;
205 }
206 while(l != ltarget){
207 /* walk the fat */
208 p = fatwalk(dos, p);
209 if(p < 0)
210 return -1;
211 l++;
212 }
213 fp->lcurrent = l;
214 fp->pcurrent = p;
215
216 /*
217 * clusters start at 2 instead of 0 (why? - presotto)
218 */
219 l = dos->dataaddr + (p-2)*dos->clustsize;
220 chat("fileaddr %ld -> %ld\n", ltarget, l);
221 return l;
222 }
223
224 /*
225 * read from a dos file
226 */
227 long
dosread(Dosfile * fp,void * a,long n)228 dosread(Dosfile *fp, void *a, long n)
229 {
230 long addr;
231 long rv;
232 int i;
233 int off;
234 Clustbuf *p;
235 uchar *from, *to;
236
237 if((fp->attr & DOSDIR) == 0){
238 if(fp->offset >= fp->length)
239 return 0;
240 if(fp->offset+n > fp->length)
241 n = fp->length - fp->offset;
242 }
243
244 to = a;
245 for(rv = 0; rv < n; rv+=i){
246 /*
247 * read the cluster
248 */
249 addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
250 if(addr < 0)
251 return -1;
252 p = getclust(fp->dos, addr);
253 if(p == 0)
254 return -1;
255
256 /*
257 * copy the bytes we need
258 */
259 off = fp->offset % fp->dos->clustbytes;
260 from = &p->iobuf[off];
261 i = n - rv;
262 if(i > fp->dos->clustbytes - off)
263 i = fp->dos->clustbytes - off;
264 memmove(to, from, i);
265 to += i;
266 fp->offset += i;
267 }
268
269 return rv;
270 }
271
272 /*
273 * walk a directory returns
274 * -1 if something went wrong
275 * 0 if not found
276 * 1 if found
277 */
278 int
doswalk(File * f,char * name)279 doswalk(File *f, char *name)
280 {
281 Dosdir d;
282 long n;
283 Dosfile *file;
284
285 chat("doswalk %s\n", name);
286
287 file = &f->dos;
288
289 if((file->attr & DOSDIR) == 0){
290 chat("walking non-directory!\n");
291 return -1;
292 }
293
294 setname(file, name);
295
296 file->offset = 0; /* start at the beginning */
297 while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
298 chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext);
299 if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
300 continue;
301 if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
302 continue;
303 if(d.attr & DOSVLABEL){
304 chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext);
305 continue;
306 }
307 file->attr = d.attr;
308 file->pstart = GSHORT(d.start);
309 if (file->dos->fatbits == 32)
310 file->pstart |= GSHORT(d.highstart) << 16;
311 file->length = GLONG(d.length);
312 file->pcurrent = 0;
313 file->lcurrent = 0;
314 file->offset = 0;
315 return 1;
316 }
317 return n >= 0 ? 0 : -1;
318 }
319
320 void
lowercase(char * s)321 lowercase(char *s)
322 {
323 for (; *s != '\0'; s++)
324 if (*s >= 'A' && *s <= 'Z')
325 *s -= 'A' - 'a';
326 }
327
328 void
trim(char * s,int len)329 trim(char *s, int len)
330 {
331 while(len > 0 && s[len-1] == ' ')
332 s[--len] = '\0';
333 }
334
335 /*
336 * read a directory and return the file names in a malloced
337 * array whose address is stored through nmarray.
338 * -1 if something went wrong
339 * else number of dir. entries
340 */
341 int
dosdirread(File * f,char *** nmarray)342 dosdirread(File *f, char ***nmarray)
343 {
344 int entries;
345 long i;
346 char buf[Dosfilemax+1+Dosextmax+1];
347 char **nms;
348 Dosdir d;
349 Dosfile *file;
350
351 chat("dosdirread\n");
352 file = &f->dos;
353 if((file->attr & DOSDIR) == 0){
354 chat("walking non-directory!\n");
355 return -1;
356 }
357
358 /* allocate the array of char*s */
359 file->offset = 0; /* start at the beginning */
360 for(entries = 0; dosread(file, &d, sizeof d) == sizeof d; entries++)
361 ;
362 nms = smalloc(sizeof(char *) * (entries + 1));
363
364 /* populate the array */
365 file->offset = 0; /* rewind */
366 for(i = 0; i < entries && dosread(file, &d, sizeof d) == sizeof d; ){
367 trim((char *)d.name, Dosfilemax);
368 trim((char *)d.ext, Dosextmax);
369 if (d.name[0] == '\0')
370 continue;
371 if (d.ext[0] == '\0')
372 kstrdup(&nms[i], (char *)d.name);
373 else {
374 snprint(buf, sizeof buf, "%.*s.%.*s",
375 Dosfilemax, (char *)d.name,
376 Dosextmax, (char *)d.ext);
377 kstrdup(&nms[i], buf);
378 }
379 lowercase(nms[i++]);
380 }
381 *nmarray = nms;
382 return 0;
383 }
384
385 /*
386 * instructions that boot blocks can start with
387 */
388 #define JMPSHORT 0xeb
389 #define JMPNEAR 0xe9
390
391 /*
392 * read in a segment
393 */
394 long
dosreadseg(File * f,void * va,long len)395 dosreadseg(File *f, void *va, long len)
396 {
397 char *a;
398 long n, sofar;
399 Dosfile *fp;
400
401 fp = &f->dos;
402 a = va;
403 for(sofar = 0; sofar < len; sofar += n){
404 n = 8*1024;
405 if(len - sofar < n)
406 n = len - sofar;
407 n = dosread(fp, a + sofar, n);
408 if(n <= 0)
409 break;
410 print(".");
411 }
412 return sofar;
413 }
414
415 int
dosinit(Bootfs * fs,char * disk)416 dosinit(Bootfs *fs, char *disk)
417 {
418 Clustbuf *p;
419 Dosboot *b;
420 int i;
421 Dos *dos;
422 Dosfile *root;
423
424 chat("dosinit0 %p %s\n", fs, fs->disk);
425
426 fs->disk = disk;
427 fs->devch = namecopen(disk, OREAD);
428 if (fs->devch == nil) {
429 print("dosinit: can't open %s\n", disk);
430 return -1;
431 }
432
433 dos = &fs->dos;
434 /* defaults till we know better */
435 dos->sectsize = 512;
436 dos->clustsize = 1;
437
438 /* get first sector */
439 p = getclust(dos, 0);
440 if(p == 0){
441 chat("can't read boot block\n");
442 return -1;
443 }
444
445 chat("dosinit0a\n");
446
447 p->dos = 0; /* don't cache this block */
448 b = (Dosboot *)p->iobuf;
449 if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
450 chat("no dos file system %x %x %x %x\n",
451 b->magic[0], b->magic[1], b->magic[2], b->magic[3]);
452 return -1;
453 }
454
455 if(chatty)
456 bootdump(b);
457
458 if(b->clustsize == 0) {
459 unreasonable:
460 if(chatty){
461 print("unreasonable FAT BPB: ");
462 for(i=0; i<3+8+2+1; i++)
463 print(" %.2ux", p->iobuf[i]);
464 print("\n");
465 }
466 return -1;
467 }
468
469 chat("dosinit1\n");
470
471 /*
472 * Determine the systems' wondrous properties.
473 * There are heuristics here, but there's no real way
474 * of knowing if this is a reasonable FAT.
475 */
476 dos->fatbits = 0;
477 dos->sectsize = GSHORT(b->sectsize);
478 if(dos->sectsize & 0xFF)
479 goto unreasonable;
480 dos->clustsize = b->clustsize;
481 dos->clustbytes = dos->sectsize*dos->clustsize;
482 dos->nresrv = GSHORT(b->nresrv);
483 dos->nfats = b->nfats;
484 dos->fatsize = GSHORT(b->fatsize);
485 dos->rootsize = GSHORT(b->rootsize);
486 dos->volsize = GSHORT(b->volsize);
487 if(dos->volsize == 0)
488 dos->volsize = GLONG(b->bigvolsize);
489 dos->mediadesc = b->mediadesc;
490 if(dos->fatsize == 0) {
491 chat("fat32\n");
492 dos->rootsize = 0;
493 dos->fatsize = GLONG(b->bigfatsize);
494 dos->fatbits = 32;
495 }
496 dos->fataddr = dos->nresrv;
497 if (dos->rootsize == 0) {
498 dos->rootaddr = 0;
499 dos->rootclust = GLONG(b->rootdirstartclust);
500 dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize;
501 } else {
502 dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
503 i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
504 i = i/dos->sectsize;
505 dos->dataaddr = dos->rootaddr + i;
506 }
507 dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
508 if(dos->fatbits != 32) {
509 if(dos->fatclusters < 4087)
510 dos->fatbits = 12;
511 else
512 dos->fatbits = 16;
513 }
514 dos->freeptr = 2;
515
516 if(dos->clustbytes < 512 || dos->clustbytes > 64*1024)
517 goto unreasonable;
518
519 chat("dosinit2\n");
520
521 /*
522 * set up the root
523 */
524
525 fs->root.fs = fs;
526 root = &fs->root.dos;
527 root->dos = dos;
528 root->pstart = dos->rootsize == 0 ? dos->rootclust : 0;
529 root->pcurrent = root->lcurrent = 0;
530 root->offset = 0;
531 root->attr = DOSDIR;
532 root->length = dos->rootsize*sizeof(Dosdir);
533
534 chat("dosinit3\n");
535
536 fs->read = dosreadseg;
537 fs->walk = doswalk;
538 return 0;
539 }
540
541 static void
bootdump(Dosboot * b)542 bootdump(Dosboot *b)
543 {
544 if(chatty == 0)
545 return;
546 print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ",
547 b->magic[0], b->magic[1], b->magic[2]);
548 print("version: \"%8.8s\"\n", (char*)b->version);
549 print("sectsize: %d ", GSHORT(b->sectsize));
550 print("allocsize: %d ", b->clustsize);
551 print("nresrv: %d ", GSHORT(b->nresrv));
552 print("nfats: %d\n", b->nfats);
553 print("rootsize: %d ", GSHORT(b->rootsize));
554 print("volsize: %d ", GSHORT(b->volsize));
555 print("mediadesc: 0x%2.2x\n", b->mediadesc);
556 print("fatsize: %d ", GSHORT(b->fatsize));
557 print("trksize: %d ", GSHORT(b->trksize));
558 print("nheads: %d ", GSHORT(b->nheads));
559 print("nhidden: %d ", GLONG(b->nhidden));
560 print("bigvolsize: %d\n", GLONG(b->bigvolsize));
561 /*
562 print("driveno: %d\n", b->driveno);
563 print("reserved0: 0x%2.2x\n", b->reserved0);
564 print("bootsig: 0x%2.2x\n", b->bootsig);
565 print("volid: 0x%8.8x\n", GLONG(b->volid));
566 print("label: \"%11.11s\"\n", b->label);
567 */
568 }
569
570
571 /*
572 * set up a dos file name
573 */
574 static void
setname(Dosfile * fp,char * from)575 setname(Dosfile *fp, char *from)
576 {
577 char *to;
578
579 to = fp->name;
580 for(; *from && to-fp->name < 8; from++, to++){
581 if(*from == '.'){
582 from++;
583 break;
584 }
585 if(*from >= 'a' && *from <= 'z')
586 *to = *from + 'A' - 'a';
587 else
588 *to = *from;
589 }
590 while(to - fp->name < 8)
591 *to++ = ' ';
592
593 /* from might be 12345678.123: don't save the '.' in ext */
594 if(*from == '.')
595 from++;
596
597 to = fp->ext;
598 for(; *from && to-fp->ext < 3; from++, to++){
599 if(*from >= 'a' && *from <= 'z')
600 *to = *from + 'A' - 'a';
601 else
602 *to = *from;
603 }
604 while(to-fp->ext < 3)
605 *to++ = ' ';
606
607 chat("name is %8.8s.%3.3s\n", fp->name, fp->ext);
608 }
609