#include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "sd.h" #include "fs.h" enum { Npart = 32 }; uchar *mbrbuf, *partbuf; int nbuf; #define trace 0 int tsdbio(SDunit *unit, SDpart *part, void *a, vlong off, int mbr) { uchar *b; if(sdbio(unit, part, a, unit->secsize, off) != unit->secsize){ if(trace) print("%s: read %lud at %lld failed\n", unit->name, unit->secsize, (vlong)part->start*unit->secsize+off); return -1; } b = a; if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){ if(trace) print("%s: bad magic %.2ux %.2ux at %lld\n", unit->name, b[0x1FE], b[0x1FF], (vlong)part->start*unit->secsize+off); return -1; } return 0; } /* * read partition table. The partition table is just ascii strings. */ #define MAGIC "plan9 partitions" static void oldp9part(SDunit *unit) { SDpart *pp; char *field[3], *line[Npart+1]; ulong n, start, end; int i; /* * We have some partitions already. */ pp = &unit->part[unit->npart]; /* * We prefer partition tables on the second to last sector, * but some old disks use the last sector instead. */ strcpy(pp->name, "partition"); pp->start = unit->sectors - 2; pp->end = unit->sectors - 1; if(tsdbio(unit, pp, partbuf, 0, 0) < 0) return; if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) { /* not found on 2nd last sector; look on last sector */ pp->start++; pp->end++; if(tsdbio(unit, pp, partbuf, 0, 0) < 0) return; if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) return; print("%s: using old plan9 partition table on last sector\n", unit->name); }else print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name); /* we found a partition table, so add a partition partition */ unit->npart++; partbuf[unit->secsize-1] = '\0'; /* * parse partition table */ n = getfields((char*)partbuf, line, Npart+1, '\n'); if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){ for(i = 1; i < n && unit->npart < SDnpart; i++){ if(getfields(line[i], field, 3, ' ') != 3) break; start = strtoul(field[1], 0, 0); end = strtoul(field[2], 0, 0); if(start >= end || end > unit->sectors) break; sdaddpart(unit, field[0], start, end); } } } static void p9part(SDunit *unit, char *name) { SDpart *p; char *field[4], *line[Npart+1]; ulong start, end; int i, n; p = sdfindpart(unit, name); if(p == nil) return; if(tsdbio(unit, p, partbuf, unit->secsize, 0) < 0) return; partbuf[unit->secsize-1] = '\0'; if(strncmp((char*)partbuf, "part ", 5) != 0) return; n = getfields((char*)partbuf, line, Npart+1, '\n'); if(n == 0) return; for(i = 0; i < n && unit->npart < SDnpart; i++){ if(strncmp(line[i], "part ", 5) != 0) break; if(getfields(line[i], field, 4, ' ') != 4) break; start = strtoul(field[2], 0, 0); end = strtoul(field[3], 0, 0); if(start >= end || end > unit->sectors) break; sdaddpart(unit, field[1], p->start+start, p->start+end); } } int isdos(int t) { return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X; } int isextend(int t) { return t==EXTEND || t==EXTHUGE || t==LEXTEND; } /* * Fetch the first dos and all plan9 partitions out of the MBR partition table. * We return -1 if we did not find a plan9 partition. */ static int mbrpart(SDunit *unit) { ulong mbroffset; Dospart *dp; ulong start, end; ulong epart, outer, inner; int havedos, i, nplan9; char name[10]; if(tsdbio(unit, &unit->part[0], mbrbuf, 0, 1) < 0) return -1; mbroffset = 0; dp = (Dospart*)&mbrbuf[0x1BE]; for(i=0; i<4; i++, dp++) if(dp->type == DMDDO) { mbroffset = 63*512; if(trace) print("DMDDO partition found\n"); if(tsdbio(unit, &unit->part[0], mbrbuf, mbroffset, 1) < 0) return -1; i = -1; /* start over */ } nplan9 = 0; havedos = 0; epart = 0; dp = (Dospart*)&mbrbuf[0x1BE]; if(trace) print("%s mbr ", unit->name); for(i=0; i<4; i++, dp++) { if(trace) print("dp %d...", dp->type); start = mbroffset+GLONG(dp->start); end = start+GLONG(dp->len); if(dp->type == PLAN9) { if(nplan9 == 0) strcpy(name, "plan9"); else sprint(name, "plan9.%d", nplan9); sdaddpart(unit, name, start, end); p9part(unit, name); nplan9++; } /* * We used to take the active partition (and then the first * when none are active). We have to take the first here, * so that the partition we call ``dos'' agrees with the * partition disk/fdisk calls ``dos''. */ if(havedos==0 && isdos(dp->type)){ havedos = 1; sdaddpart(unit, "dos", start, end); } if(isextend(dp->type)){ epart = start; if(trace) print("link %lud...", epart); } } if(trace) print("\n"); /* * Search through the chain of extended partition tables. */ outer = epart; while(epart != 0) { if(trace) print("%s ext %lud ", unit->name, epart); if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)epart*unit->secsize, 1) < 0) break; inner = epart; epart = 0; dp = (Dospart*)&mbrbuf[0x1BE]; for(i=0; i<4; i++, dp++) { if(trace) print("dp %d...", dp->type); start = GLONG(dp->start); if(dp->type == PLAN9){ start += inner; end = start+GLONG(dp->len); if(nplan9 == 0) strcpy(name, "plan9"); else sprint(name, "plan9.%d", nplan9); sdaddpart(unit, name, start, end); p9part(unit, name); nplan9++; } if(havedos==0 && isdos(dp->type)){ start += inner; end = start+GLONG(dp->len); havedos = 1; sdaddpart(unit, "dos", start, end); } if(isextend(dp->type)){ epart = start + outer; if(trace) print("link %lud...", epart); } } if(trace) print("\n"); } return nplan9 ? 0 : -1; } /* * To facilitate booting from CDs, we create a partition for * the boot floppy image embedded in a bootable CD. */ static int part9660(SDunit *unit) { uchar buf[2048]; ulong a, n; uchar *p; if(unit->secsize != 2048) return -1; if(sdbio(unit, &unit->part[0], buf, 2048, 17*2048) < 0) return -1; if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0) return -1; p = buf+0x47; a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); if(sdbio(unit, &unit->part[0], buf, 2048, a*2048) < 0) return -1; if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0 || memcmp(buf+30, "\x55\xAA", 2) != 0 || buf[0x20] != 0x88) return -1; p = buf+0x28; a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); switch(buf[0x21]){ case 0x01: n = 1200*1024; break; case 0x02: n = 1440*1024; break; case 0x03: n = 2880*1024; break; default: return -1; } n /= 2048; print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n); sdaddpart(unit, "cdboot", a, a+n); return 0; } enum { NEW = 1<<0, OLD = 1<<1 }; void partition(SDunit *unit) { int type; char *p; if(unit->part == 0) return; if(part9660(unit) == 0) return; p = getconf("partition"); if(p == nil) p = defaultpartition; if(p != nil && strncmp(p, "new", 3) == 0) type = NEW; else if(p != nil && strncmp(p, "old", 3) == 0) type = OLD; else type = NEW|OLD; if(nbuf < unit->secsize) { free(mbrbuf); free(partbuf); mbrbuf = malloc(unit->secsize); partbuf = malloc(unit->secsize); if(mbrbuf==nil || partbuf==nil) { free(mbrbuf); free(partbuf); partbuf = mbrbuf = nil; nbuf = 0; return; } nbuf = unit->secsize; } if((type & NEW) && mbrpart(unit) >= 0){ /* nothing to do */; } else if(type & OLD) oldp9part(unit); }