Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * read disk partition tables, intended for early use on systems
3
 * that don't use (the new) 9load.  borrowed from old 9load.
4
 */
5
 
6
#include	"u.h"
7
#include	"../port/lib.h"
8
#include	"mem.h"
9
#include	"dat.h"
10
#include	"fns.h"
11
#include	"io.h"
12
#include	"ureg.h"
13
#include	"pool.h"
14
#include	"../port/error.h"
15
#include	"../port/netif.h"
16
#include	"dosfs.h"
17
#include	"../port/sd.h"
18
#include	"iso9660.h"
19
 
20
#define gettokens(l, a, an, del)	getfields(l, a, an, 1, del)
21
 
22
enum {
23
	Trace	= 0,
24
	Parttrace = 0,
25
	Debugboot = 0,
26
 
27
	Maxsec	= 2048,
28
	Normsec	= 512,			/* mag disks */
29
 
30
	/* from devsd.c */
31
	PartLOG		= 8,
32
	NPart		= (1<<PartLOG),
33
};
34
 
35
typedef struct PSDunit PSDunit;
36
struct PSDunit {
37
	SDunit;
38
	Chan	*ctlc;
39
	Chan	*data;
40
};
41
 
42
static uchar *mbrbuf, *partbuf;
43
static char buf[128], buf2[128];
44
 
45
static void
46
psdaddpart(PSDunit* unit, char* name, uvlong start, uvlong end)
47
{
48
	int len, nw;
49
 
50
	sdaddpart(unit, name, start, end);
51
 
52
	/* update devsd's in-memory partition table. */
53
	len = snprint(buf, sizeof buf, "part %s %lld %lld\n", name, start, end);
54
	nw = devtab[unit->ctlc->type]->write(unit->ctlc, buf, len,
55
		unit->ctlc->offset);
56
	if (nw != len)
57
		print("can't update devsd's partition table\n");
58
	if (Debugboot)
59
		print("part %s %lld %lld\n", name, start, end);
60
}
61
 
62
static long
63
psdread(PSDunit *unit, SDpart *pp, void* va, long len, vlong off)
64
{
65
	long l, secsize;
66
	uvlong bno, nb;
67
 
68
	/*
69
	 * Check the request is within partition bounds.
70
	 */
71
	secsize = unit->secsize;
72
	if (secsize == 0)
73
		panic("psdread: %s: zero sector size", unit->name);
74
	bno = off/secsize + pp->start;
75
	nb = (off+len+secsize-1)/secsize + pp->start - bno;
76
	if(bno+nb > pp->end)
77
		nb = pp->end - bno;
78
	if(bno >= pp->end || nb == 0)
79
		return 0;
80
 
81
	unit->data->offset = bno * secsize;
82
	l = myreadn(unit->data, va, len);
83
	if (l < 0)
84
		return 0;
85
	return l;
86
}
87
 
88
static int
89
sdreadblk(PSDunit *unit, SDpart *part, void *a, vlong off, int mbr)
90
{
91
	uchar *b;
92
 
93
	assert(a);			/* sdreadblk */
94
	if(psdread(unit, part, a, unit->secsize, off) != unit->secsize){
95
		if(Trace)
96
			print("%s: read %lud at %lld failed\n", unit->name,
97
				unit->secsize, (vlong)part->start*unit->secsize+off);
98
		return -1;
99
	}
100
	b = a;
101
	if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
102
		if(Trace)
103
			print("%s: bad magic %.2ux %.2ux at %lld\n",
104
				unit->name, b[0x1FE], b[0x1FF],
105
				(vlong)part->start*unit->secsize+off);
106
		return -1;
107
	}
108
	return 0;
109
}
110
 
111
/*
112
 *  read partition table.  The partition table is just ascii strings.
113
 */
114
#define MAGIC "plan9 partitions"
115
static void
116
oldp9part(PSDunit *unit)
117
{
118
	SDpart *pp;
119
	char *field[3], *line[NPart+1];
120
	ulong n;
121
	uvlong start, end;
122
	int i;
123
	static SDpart fakepart;
124
 
125
	/*
126
	 * We prefer partition tables on the second to last sector,
127
	 * but some old disks use the last sector instead.
128
	 */
129
 
130
	pp = &fakepart;
131
	kstrdup(&pp->name, "partition");
132
	pp->start = unit->sectors - 2;
133
	pp->end = unit->sectors - 1;
134
 
135
	if(Debugboot)
136
		print("oldp9part %s\n", unit->name);
137
	if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
138
		return;
139
 
140
	if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
141
		/* not found on 2nd last sector; look on last sector */
142
		pp->start++;
143
		pp->end++;
144
		if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
145
			return;
146
		if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
147
			return;
148
		print("%s: using old plan9 partition table on last sector\n", unit->name);
149
	}else
150
		print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
151
 
152
	/* we found a partition table, so add a partition partition */
153
	psdaddpart(unit, pp->name, pp->start, pp->end);
154
 
155
	/*
156
	 * parse partition table
157
	 */
158
	partbuf[unit->secsize-1] = '\0';
159
	n = gettokens((char*)partbuf, line, NPart+1, "\n");
160
	if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0)
161
		for(i = 1; i < n; i++){
162
			if(gettokens(line[i], field, 3, " ") != 3)
163
				break;
164
			start = strtoull(field[1], 0, 0);
165
			end = strtoull(field[2], 0, 0);
166
			if(start >= end || end > unit->sectors)
167
				break;
168
			psdaddpart(unit, field[0], start, end);
169
		}
170
}
171
 
172
static SDpart*
173
sdfindpart(PSDunit *unit, char *name)
174
{
175
	int i;
176
 
177
	if(Parttrace)
178
		print("findpart %d %s %s: ", unit->npart, unit->name, name);
179
	for(i=0; i<unit->npart; i++) {
180
		if(Parttrace)
181
			print("%s...", unit->part[i].name);
182
		if(strcmp(unit->part[i].name, name) == 0){
183
			if(Parttrace)
184
				print("\n");
185
			return &unit->part[i];
186
		}
187
	}
188
	if(Parttrace)
189
		print("not found\n");
190
	return nil;
191
}
192
 
193
/*
194
 * look for a plan 9 partition table on drive `unit' in the second
195
 * sector (sector 1) of partition `name'.
196
 * if found, add the partitions defined in the table.
197
 */
198
static void
199
p9part(PSDunit *unit, char *name)
200
{
201
	SDpart *p;
202
	char *field[4], *line[NPart+1];
203
	uvlong start, end;
204
	int i, n;
205
 
206
	if(Debugboot)
207
		print("p9part %s %s\n", unit->name, name);
208
	p = sdfindpart(unit, name);
209
	if(p == nil)
210
		return;
211
 
212
	if(sdreadblk(unit, p, partbuf, unit->secsize, 0) < 0)
213
		return;
214
	partbuf[unit->secsize-1] = '\0';
215
 
216
	if(strncmp((char*)partbuf, "part ", 5) != 0)
217
		return;
218
 
219
	n = gettokens((char*)partbuf, line, NPart+1, "\n");
220
	if(n == 0)
221
		return;
222
	for(i = 0; i < n; i++){
223
		if(strncmp(line[i], "part ", 5) != 0)
224
			break;
225
		if(gettokens(line[i], field, 4, " ") != 4)
226
			break;
227
		start = strtoull(field[2], 0, 0);
228
		end   = strtoull(field[3], 0, 0);
229
		if(start >= end || end > unit->sectors)
230
			break;
231
		psdaddpart(unit, field[1], p->start+start, p->start+end);
232
	}
233
}
234
 
235
static int
236
isdos(int t)
237
{
238
	return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
239
}
240
 
241
static int
242
isextend(int t)
243
{
244
	return t==EXTEND || t==EXTHUGE || t==LEXTEND;
245
}
246
 
247
/*
248
 * Fetch the first dos and all plan9 partitions out of the MBR partition table.
249
 * We return -1 if we did not find a plan9 partition.
250
 */
251
static int
252
mbrpart(PSDunit *unit)
253
{
254
	Dospart *dp;
255
	uvlong taboffset, start, end;
256
	uvlong firstxpart, nxtxpart;
257
	int havedos, i, nplan9;
258
	char name[10];
259
 
260
	taboffset = 0;
261
	dp = (Dospart*)&mbrbuf[0x1BE];
262
	{
263
		/* get the MBR (allowing for DMDDO) */
264
		if(sdreadblk(unit, &unit->part[0], mbrbuf,
265
		    (vlong)taboffset * unit->secsize, 1) < 0)
266
			return -1;
267
		for(i=0; i<4; i++)
268
			if(dp[i].type == DMDDO) {
269
				if(Trace)
270
					print("DMDDO partition found\n");
271
				taboffset = 63;
272
				if(sdreadblk(unit, &unit->part[0], mbrbuf,
273
				    (vlong)taboffset * unit->secsize, 1) < 0)
274
					return -1;
275
				i = -1;	/* start over */
276
			}
277
	}
278
 
279
	/*
280
	 * Read the partitions, first from the MBR and then
281
	 * from successive extended partition tables.
282
	 */
283
	nplan9 = 0;
284
	havedos = 0;
285
	firstxpart = 0;
286
	for(;;) {
287
		if(sdreadblk(unit, &unit->part[0], mbrbuf,
288
		    (vlong)taboffset * unit->secsize, 1) < 0)
289
			return -1;
290
		if(Trace) {
291
			if(firstxpart)
292
				print("%s ext %llud ", unit->name, taboffset);
293
			else
294
				print("%s mbr ", unit->name);
295
		}
296
		nxtxpart = 0;
297
		for(i=0; i<4; i++) {
298
			if(Trace)
299
				print("dp %d...", dp[i].type);
300
			start = taboffset+GLONG(dp[i].start);
301
			end = start+GLONG(dp[i].len);
302
 
303
			if(dp[i].type == PLAN9) {
304
				if(nplan9 == 0)
305
					strncpy(name, "plan9", sizeof name);
306
				else
307
					snprint(name, sizeof name, "plan9.%d",
308
						nplan9);
309
				psdaddpart(unit, name, start, end);
310
				p9part(unit, name);
311
				nplan9++;
312
			}
313
 
314
			/*
315
			 * We used to take the active partition (and then the first
316
			 * when none are active).  We have to take the first here,
317
			 * so that the partition we call ``dos'' agrees with the
318
			 * partition disk/fdisk calls ``dos''.
319
			 */
320
			if(havedos==0 && isdos(dp[i].type)){
321
				havedos = 1;
322
				psdaddpart(unit, "dos", start, end);
323
			}
324
 
325
			/* nxtxpart is relative to firstxpart (or 0), not taboffset */
326
			if(isextend(dp[i].type)){
327
				nxtxpart = start-taboffset+firstxpart;
328
				if(Trace)
329
					print("link %llud...", nxtxpart);
330
			}
331
		}
332
		if(Trace)
333
			print("\n");
334
 
335
		if(!nxtxpart)
336
			break;
337
		if(!firstxpart)
338
			firstxpart = nxtxpart;
339
		taboffset = nxtxpart;
340
	}
341
	return nplan9 ? 0 : -1;
342
}
343
 
344
/*
345
 * To facilitate booting from CDs, we create a partition for
346
 * the FAT filesystem image embedded in a bootable CD.
347
 */
348
static int
349
part9660(PSDunit *unit)
350
{
351
	ulong a, n, i, j;
352
	uchar drecsz;
353
	uchar *p;
354
	uchar buf[Maxsec];
355
	Drec *rootdrec, *drec;
356
	Voldesc *v;
357
	static char stdid[] = "CD001\x01";
358
 
359
	if(unit->secsize == 0)
360
		unit->secsize = Cdsec;
361
	if(unit->secsize != Cdsec)
362
		return -1;
363
 
364
	if(psdread(unit, &unit->part[0], buf, Cdsec, VOLDESC*Cdsec) < 0)
365
		return -1;
366
	if(buf[0] != PrimaryIso ||
367
	    memcmp((char*)buf+1, stdid, sizeof stdid - 1) != 0)
368
		return -1;
369
 
370
	v = (Voldesc *)buf;
371
	rootdrec = (Drec *)v->z.desc.rootdir;
372
	assert(rootdrec);
373
	p = rootdrec->addr;
374
	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
375
	p = rootdrec->size;
376
	n = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
377
//	print("part9660: read %uld %uld\n", n, a);	/* debugging */
378
 
379
	if(n < Cdsec){
380
		print("warning: bad boot file size %ld in iso directory", n);
381
		n = Cdsec;
382
	}
383
 
384
	drec = nil;
385
	for(j = 0; j*Cdsec < n; j++){
386
		if(psdread(unit, &unit->part[0], buf, Cdsec, (a + j)*Cdsec) < 0)
387
			return -1;
388
		for(i = 0; i + j*Cdsec <= n && i < Cdsec; i += drecsz){
389
			drec = (Drec *)&buf[i];
390
			drecsz = drec->reclen;
391
			if(drecsz == 0 || drecsz + i > Cdsec)
392
				break;
393
			if(cistrncmp("bootdisk.img", (char *)drec->name, 12) == 0)
394
				goto Found;
395
		}
396
	}
397
Found:
398
	if(j*Cdsec >= n || drec == nil)
399
		return -1;
400
 
401
	p = drec->addr;
402
	a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
403
	p = drec->size;
404
	n = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
405
 
406
	print("found partition %s!9fat; %lud+%lud\n", unit->name, a, n);
407
	n /= Cdsec;
408
	psdaddpart(unit, "9fat", a, a+n);
409
	return 0;
410
}
411
 
412
enum {
413
	NEW = 1<<0,
414
	OLD = 1<<1
415
};
416
 
417
/*
418
 * read unit->data to look for partition tables.
419
 * if found, stash partitions in environment and write them to ctl too.
420
 */
421
static void
422
partition(PSDunit *unit)
423
{
424
	int type;
425
	char *p;
426
 
427
	if(unit->part == 0)
428
		return;
429
 
430
	if(part9660(unit) == 0)
431
		return;
432
 
433
	p = getconf("partition");
434
	if(p != nil && strncmp(p, "new", 3) == 0)
435
		type = NEW;
436
	else if(p != nil && strncmp(p, "old", 3) == 0)
437
		type = OLD;
438
	else
439
		type = NEW|OLD;
440
 
441
	if(mbrbuf == nil) {
442
		mbrbuf = malloc(Maxsec);
443
		partbuf = malloc(Maxsec);
444
		if(mbrbuf==nil || partbuf==nil) {
445
			free(mbrbuf);
446
			free(partbuf);
447
			partbuf = mbrbuf = nil;
448
			return;
449
		}
450
	}
451
 
452
	/*
453
	 * there might be no mbr (e.g. on a very large device), so look for
454
	 * a bare plan 9 partition table if mbrpart fails.
455
	 */
456
	if((type & NEW) && mbrpart(unit) >= 0){
457
		/* nothing to do */
458
	}
459
	else if (type & NEW)
460
		p9part(unit, "data");
461
	else if(type & OLD)
462
		oldp9part(unit);
463
}
464
 
465
static void
466
rdgeom(PSDunit *unit)
467
{
468
	int n, f, lines;
469
	char *buf, *p;
470
	char *line[64], *fld[5];
471
	char ctl[64];
472
	static char geom[] = "geometry";
473
 
474
	buf = smalloc(Maxfile + 1);
475
	strncpy(ctl, unit->name, sizeof ctl);
476
	p = strrchr(ctl, '/');
477
	if (p)
478
		strcpy(p, "/ctl");		/* was "/data" */
479
	n = readfile(ctl, buf, Maxfile);
480
	if (n < 0) {
481
		print("rdgeom: can't read %s\n", ctl);
482
		free(buf);
483
		return;
484
	}
485
	buf[n] = 0;
486
 
487
	lines = getfields(buf, line, nelem(line), 0, "\r\n");
488
	for (f = 0; f < lines; f++)
489
		if (tokenize(line[f], fld, nelem(fld)) >= 3 &&
490
		    strcmp(fld[0], geom) == 0)
491
			break;
492
	if(f < lines){
493
		unit->sectors = strtoull(fld[1], nil, 0);
494
		unit->secsize = strtoull(fld[2], nil, 0);
495
	}
496
	if (f >= lines || unit->sectors == 0){
497
		/* no geometry line, so fake it */
498
		unit->secsize = Cdsec;
499
		unit->sectors = ~0ull / unit->secsize;
500
	}
501
	if(unit->secsize == 0)
502
		print("rdgeom: %s: zero sector size read from ctl file\n",
503
			unit->name);
504
	free(buf);
505
	unit->ctlc->offset = 0;
506
}
507
 
508
static void
509
setpartitions(char *name, Chan *ctl, Chan *data)
510
{
511
	PSDunit sdunit;
512
	PSDunit *unit;
513
	SDpart *part0;
514
 
515
	unit = &sdunit;
516
	memset(unit, 0, sizeof *unit);
517
	unit->ctlc = ctl;
518
	unit->data = data;
519
 
520
	unit->secsize = Normsec;	/* default: won't work for CDs */
521
	unit->sectors = ~0ull / unit->secsize;
522
	kstrdup(&unit->name, name);
523
	rdgeom(unit);
524
	unit->part = mallocz(sizeof(SDpart) * SDnpart, 1);
525
	unit->npart = SDnpart;
526
 
527
	part0 = &unit->part[0];
528
	part0->end = unit->sectors - 1;
529
	kstrdup(&part0->name, "data");
530
	part0->valid = 1;
531
 
532
	mbrbuf = malloc(Maxsec);
533
	partbuf = malloc(Maxsec);
534
	partition(unit);
535
	free(unit->part);
536
	unit->part = nil;
537
}
538
 
539
/*
540
 * read disk partition tables so that readnvram via factotum
541
 * can see them.
542
 */
543
int
544
readparts(char *disk)
545
{
546
	Chan *ctl, *data;
547
 
548
	snprint(buf, sizeof buf, "%s/ctl", disk);
549
	ctl  = namecopen(buf, ORDWR);
550
	snprint(buf2, sizeof buf2, "%s/data", disk);
551
	data = namecopen(buf2, OREAD);
552
	if (ctl != nil && data != nil)
553
		setpartitions(buf2, ctl, data);
554
	cclose(ctl);
555
	cclose(data);
556
	return 0;
557
}
558
 
559
/*
560
 * Leave partitions around for devsd in next kernel to pick up.
561
 * (Needed by boot process; more extensive
562
 * partitioning is done by termrc or cpurc).
563
 */
564
void
565
sdaddconf(SDunit *unit)
566
{
567
	int i;
568
	SDpart *pp;
569
 
570
	/*
571
	 * If there were no partitions (just data and partition), don't bother.
572
	 */
573
	if(unit->npart <= 1 || (unit->npart == 2 &&
574
	    strcmp(unit->part[1].name, "partition") == 0))
575
		return;
576
 
577
	addconf("%spart=", unit->name);
578
	/* skip 0, which is "data" */
579
	for(i = 1, pp = &unit->part[i]; i < unit->npart; i++, pp++)
580
		if (pp->valid)
581
			addconf("%s%s %lld %lld", i==1 ? "" : "/", pp->name,
582
				pp->start, pp->end);
583
	addconf("\n");
584
}