Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
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
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
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
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
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
321
lowercase(char *s)
322
{
323
	for (; *s != '\0'; s++)
324
		if (*s >= 'A' && *s <= 'Z')
325
			*s -= 'A' - 'a';
326
}
327
 
328
void
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
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
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
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
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
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
}