summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/prep/fdisk.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/disk/prep/fdisk.b')
-rw-r--r--appl/cmd/disk/prep/fdisk.b925
1 files changed, 925 insertions, 0 deletions
diff --git a/appl/cmd/disk/prep/fdisk.b b/appl/cmd/disk/prep/fdisk.b
new file mode 100644
index 00000000..00ecbb36
--- /dev/null
+++ b/appl/cmd/disk/prep/fdisk.b
@@ -0,0 +1,925 @@
+implement Fdisk;
+
+#
+# fdisk - edit dos disk partition table
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "disks.m";
+ disks: Disks;
+ Disk, PCpart: import disks;
+ NTentry, Toffset, TentrySize: import Disks;
+ Magic0, Magic1: import Disks;
+ readn: import disks;
+
+include "pedit.m";
+ pedit: Pedit;
+ Edit, Part: import pedit;
+
+include "arg.m";
+
+Fdisk: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Mpart: con 64;
+
+blank := 0;
+dowrite := 0;
+file := 0;
+rdonly := 0;
+doauto := 0;
+mbroffset := big 0;
+printflag := 0;
+printchs := 0;
+sec2cyl := big 0;
+written := 0;
+
+edit: ref Edit;
+stderr: ref Sys->FD;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ disks = load Disks Disks->PATH;
+ pedit = load Pedit Pedit->PATH;
+
+ sys->pctl(Sys->FORKFD, nil);
+ disks->init();
+ pedit->init();
+
+ edit = Edit.mk("cylinder");
+
+ edit.add = cmdadd;
+ edit.del = cmddel;
+ edit.okname = cmdokname;
+ edit.ext = cmdext;
+ edit.help = cmdhelp;
+ edit.sum = cmdsum;
+ edit.write = cmdwrite;
+ edit.printctl = cmdprintctl;
+
+ stderr = sys->fildes(2);
+
+ secsize := 0;
+ arg := load Arg Arg->PATH;
+ arg->init(args);
+ arg->setusage("disk/fdisk [-abfprvw] [-s sectorsize] /dev/sdC0/data");
+ while((o := arg->opt()) != 0)
+ case o {
+ 'a' =>
+ doauto++;
+ 'b' =>
+ blank++;
+ 'f' =>
+ file++;
+ 'p' =>
+ printflag++;
+ 'r' =>
+ rdonly++;
+ 's' =>
+ secsize = int arg->earg();
+ 'v' =>
+ printchs++;
+ 'w' =>
+ dowrite++;
+ * =>
+ arg->usage();
+ }
+ args = arg->argv();
+ if(len args != 1)
+ arg->usage();
+ arg = nil;
+
+ mode := Sys->ORDWR;
+ if(rdonly)
+ mode = Sys->OREAD;
+ edit.disk = Disk.open(hd args, mode, file);
+ if(edit.disk == nil) {
+ sys->fprint(stderr, "cannot open disk: %r\n");
+ exits("opendisk");
+ }
+
+ if(secsize != 0) {
+ edit.disk.secsize = secsize;
+ edit.disk.secs = edit.disk.size / big secsize;
+ }
+
+ sec2cyl = big (edit.disk.h * edit.disk.s);
+ edit.end = edit.disk.secs / sec2cyl;
+
+ findmbr(edit);
+
+ if(blank)
+ blankpart(edit);
+ else
+ rdpart(edit, big 0, big 0);
+
+ if(doauto)
+ autopart(edit);
+
+ {
+ if(dowrite)
+ edit.runcmd("w");
+
+ if(printflag)
+ edit.runcmd("P");
+
+ if(dowrite || printflag)
+ exits(nil);
+
+ sys->fprint(stderr, "cylinder = %bd bytes\n", sec2cyl*big edit.disk.secsize);
+ edit.runcmd("p");
+ for(;;) {
+ sys->fprint(stderr, ">>> ");
+ edit.runcmd(edit.getline());
+ }
+ }exception e{
+ "*" =>
+ sys->fprint(stderr, "fdisk: exception %q\n", e);
+ if(written)
+ recover(edit);
+ }
+}
+
+Active: con 16r80; # partition is active
+Primary: con 16r01; # internal flag
+
+TypeBB: con 16rFF;
+
+TypeEMPTY: con 16r00;
+TypeFAT12: con 16r01;
+TypeXENIX: con 16r02; # root
+TypeXENIXUSR: con 16r03; # usr
+TypeFAT16: con 16r04;
+TypeEXTENDED: con 16r05;
+TypeFATHUGE: con 16r06;
+TypeHPFS: con 16r07;
+TypeAIXBOOT: con 16r08;
+TypeAIXDATA: con 16r09;
+TypeOS2BOOT: con 16r0A; # OS/2 Boot Manager
+TypeFAT32: con 16r0B; # FAT 32
+TypeFAT32LBA: con 16r0C; # FAT 32 needing LBA support
+TypeEXTHUGE: con 16r0F; # FAT 32 extended partition
+TypeUNFORMATTED: con 16r16; # unformatted primary partition (OS/2 FDISK)?
+TypeHPFS2: con 16r17;
+TypeIBMRecovery: con 16r1C; # really hidden fat
+TypeCPM0: con 16r52;
+TypeDMDDO: con 16r54; # Disk Manager Dynamic Disk Overlay
+TypeGB: con 16r56; # ????
+TypeSPEEDSTOR: con 16r61;
+TypeSYSV386: con 16r63; # also HURD?
+TypeNETWARE: con 16r64;
+TypePCIX: con 16r75;
+TypeMINIX13: con 16r80; # Minix v1.3 and below
+TypeMINIX: con 16r81; # Minix v1.5+
+TypeLINUXSWAP: con 16r82;
+TypeLINUX: con 16r83;
+TypeLINUXEXT: con 16r85;
+TypeAMOEBA: con 16r93;
+TypeAMOEBABB: con 16r94;
+TypeBSD386: con 16rA5;
+TypeBSDI: con 16rB7;
+TypeBSDISWAP: con 16rB8;
+TypeOTHER: con 16rDA;
+TypeCPM: con 16rDB;
+TypeDellRecovery: con 16rDE;
+TypeSPEEDSTOR12: con 16rE1;
+TypeSPEEDSTOR16: con 16rE4;
+TypeLANSTEP: con 16rFE;
+
+Type9: con Disks->Type9;
+
+TableSize: con TentrySize*NTentry;
+Omagic: con TableSize;
+
+Type: adt {
+ desc: string;
+ name: string;
+};
+
+Dospart: adt {
+ p: ref Part;
+ pc: ref PCpart;
+ primary: int;
+ lba: big; # absolute address
+ size: big;
+};
+
+Recover: adt {
+ table: array of byte; # [TableSize+2] copy of table and magic
+ lba: big; # where it came from
+};
+
+types: array of Type = array[256] of {
+ TypeEMPTY => ( "EMPTY", "" ),
+ TypeFAT12 => ( "FAT12", "dos" ),
+ TypeFAT16 => ( "FAT16", "dos" ),
+ TypeFAT32 => ( "FAT32", "dos" ),
+ TypeFAT32LBA => ( "FAT32LBA", "dos" ),
+ TypeEXTHUGE => ( "EXTHUGE", "" ),
+ TypeIBMRecovery => ( "IBMRECOVERY", "ibm" ),
+ TypeEXTENDED => ( "EXTENDED", "" ),
+ TypeFATHUGE => ( "FATHUGE", "dos" ),
+ TypeBB => ( "BB", "bb" ),
+
+ TypeXENIX => ( "XENIX", "xenix" ),
+ TypeXENIXUSR => ( "XENIX USR", "xenixusr" ),
+ TypeHPFS => ( "HPFS", "ntfs" ),
+ TypeAIXBOOT => ( "AIXBOOT", "aixboot" ),
+ TypeAIXDATA => ( "AIXDATA", "aixdata" ),
+ TypeOS2BOOT => ( "OS/2BOOT", "os2boot" ),
+ TypeUNFORMATTED => ( "UNFORMATTED", "" ),
+ TypeHPFS2 => ( "HPFS2", "hpfs2" ),
+ TypeCPM0 => ( "CPM0", "cpm0" ),
+ TypeDMDDO => ( "DMDDO", "dmdd0" ),
+ TypeGB => ( "GB", "gb" ),
+ TypeSPEEDSTOR => ( "SPEEDSTOR", "speedstor" ),
+ TypeSYSV386 => ( "SYSV386", "sysv386" ),
+ TypeNETWARE => ( "NETWARE", "netware" ),
+ TypePCIX => ( "PCIX", "pcix" ),
+ TypeMINIX13 => ( "MINIXV1.3", "minix13" ),
+ TypeMINIX => ( "MINIXV1.5", "minix15" ),
+ TypeLINUXSWAP => ( "LINUXSWAP", "linuxswap" ),
+ TypeLINUX => ( "LINUX", "linux" ),
+ TypeLINUXEXT => ( "LINUXEXTENDED", "" ),
+ TypeAMOEBA => ( "AMOEBA", "amoeba" ),
+ TypeAMOEBABB => ( "AMOEBABB", "amoebaboot" ),
+ TypeBSD386 => ( "BSD386", "bsd386" ),
+ TypeBSDI => ( "BSDI", "bsdi" ),
+ TypeBSDISWAP => ( "BSDISWAP", "bsdiswap" ),
+ TypeOTHER => ( "OTHER", "other" ),
+ TypeCPM => ( "CPM", "cpm" ),
+ TypeDellRecovery => ( "DELLRECOVERY", "dell" ),
+ TypeSPEEDSTOR12 => ( "SPEEDSTOR12", "speedstor" ),
+ TypeSPEEDSTOR16 => ( "SPEEDSTOR16", "speedstor" ),
+ TypeLANSTEP => ( "LANSTEP", "lanstep" ),
+
+ Type9 => ( "PLAN9", "plan9" ),
+
+ * => (nil, nil),
+};
+
+dosparts: list of ref Dospart;
+
+tag2part(p: ref Part): ref Dospart
+{
+ for(l := dosparts; l != nil; l = tl l)
+ if((hd l).p.tag == p.tag)
+ return hd l;
+ raise "tag2part: cannot happen";
+}
+
+typestr0(ptype: int): string
+{
+ if(ptype < 0 || ptype >= len types || types[ptype].desc == nil)
+ return sys->sprint("type %d", ptype);
+ return types[ptype].desc;
+}
+
+gettable(disk: ref Disk, addr: big, mbr: int): array of byte
+{
+ table := array[TableSize+2] of {* => byte 0};
+ diskread(disk, table, len table, addr, Toffset);
+ if(mbr){
+ # the informal specs say all must have this but apparently not, only mbr
+ if(int table[Omagic] != Magic0 || int table[Omagic+1] != Magic1)
+ sysfatal("did not find master boot record");
+ }
+ return table;
+}
+
+diskread(disk: ref Disk, data: array of byte, ndata: int, sec: big, off: int)
+{
+ a := sec*big disk.secsize + big off;
+ if(sys->seek(disk.fd, a, 0) != a)
+ sysfatal(sys->sprint("diskread seek %bud.%ud: %r", sec, off));
+ if(readn(disk.fd, data, ndata) != ndata)
+ sysfatal(sys->sprint("diskread %ud at %bud.%ud: %r", ndata, sec, off));
+}
+
+puttable(disk: ref Disk, table: array of byte, sec: big): int
+{
+ return diskwrite(disk, table, len table, sec, Toffset);
+}
+
+diskwrite(disk: ref Disk, data: array of byte, ndata: int, sec: big, off: int): int
+{
+ written = 1;
+ a := sec*big disk.secsize + big off;
+ if(sys->seek(disk.wfd, a, 0) != a ||
+ sys->write(disk.wfd, data, ndata) != ndata){
+ sys->fprint(stderr, "write %d bytes at %bud.%ud failed: %r\n", ndata, sec, off);
+ return -1;
+ }
+ return 0;
+}
+
+partgen := 0;
+parttag := 0;
+
+mkpart(name: string, primary: int, lba: big, size: big, pcpart: ref PCpart): ref Dospart
+{
+ p := ref Dospart;
+ if(name == nil){
+ if(primary)
+ c := 'p';
+ else
+ c = 's';
+ name = sys->sprint("%c%d", c, ++partgen);
+ }
+
+ if(pcpart != nil)
+ p.pc = pcpart;
+ else
+ p.pc = ref PCpart(0, 0, big 0, big 0, big 0);
+
+ p.primary = primary;
+ p.p = ref Part; # TO DO
+ p.p.name = name;
+ p.p.start = lba/sec2cyl;
+ p.p.end = (lba+size)/sec2cyl;
+ p.p.ctlstart = lba;
+ p.p.ctlend = lba+size;
+ p.p.tag = ++parttag;
+ p.lba = lba; # absolute lba
+ p.size = size;
+ dosparts = p :: dosparts;
+ return p;
+}
+
+#
+# Recovery takes care of remembering what the various tables
+# looked like when we started, attempting to restore them when
+# we are finished.
+#
+rtabs: list of ref Recover;
+
+addrecover(t: array of byte, lba: big)
+{
+ tc := array[TableSize+2] of byte;
+ tc[0:] = t[0:len tc];
+ rtabs = ref Recover(tc, lba) :: rtabs;
+}
+
+recover(edit: ref Edit)
+{
+ err := 0;
+ for(rl := rtabs; rl != nil; rl = tl rl){
+ r := hd rl;
+ if(puttable(edit.disk, r.table, r.lba) < 0)
+ err = 1;
+ }
+ if(err) {
+ sys->fprint(stderr, "warning: some writes failed during restoration of old partition tables\n");
+ exits("inconsistent");
+ } else
+ sys->fprint(stderr, "restored old partition tables\n");
+
+ ctlfd := edit.disk.ctlfd;
+ if(ctlfd != nil){
+ offset := edit.disk.offset;
+ for(i:=0; i<len edit.part; i++)
+ if(edit.part[i].ctlname != nil && sys->fprint(ctlfd, "delpart %s", edit.part[i].ctlname)<0)
+ sys->fprint(stderr, "delpart failed: %s: %r", edit.part[i].ctlname);
+ for(i=0; i<len edit.ctlpart; i++)
+ if(edit.part[i].name != nil && sys->fprint(ctlfd, "delpart %s", edit.ctlpart[i].name)<0)
+ sys->fprint(stderr, "delpart failed: %s: %r", edit.ctlpart[i].name);
+ for(i=0; i<len edit.ctlpart; i++){
+ if(sys->fprint(ctlfd, "part %s %bd %bd", edit.ctlpart[i].name,
+ edit.ctlpart[i].start+offset, edit.ctlpart[i].end+offset) < 0){
+ sys->fprint(stderr, "restored disk partition table but not kernel; reboot\n");
+ exits("inconsistent");
+ }
+ }
+ }
+ exits("restored");
+}
+
+#
+# Read the partition table (including extended partition tables)
+# from the disk into the part array.
+#
+rdpart(edit: ref Edit, lba: big, xbase: big)
+{
+ if(xbase == big 0)
+ xbase = lba; # extended partition in mbr sets the base
+
+ table := gettable(edit.disk, mbroffset+lba, lba == big 0);
+ addrecover(table, mbroffset+lba);
+
+ for(tp := 0; tp<TableSize; tp += TentrySize){
+ dp := PCpart.extract(table[tp:], edit.disk);
+ case dp.ptype {
+ TypeEMPTY =>
+ ;
+ TypeEXTENDED or
+ TypeEXTHUGE or
+ TypeLINUXEXT =>
+ rdpart(edit, xbase+dp.offset, xbase);
+ * =>
+ p := mkpart(nil, lba==big 0, lba+dp.offset, dp.size, ref dp);
+ if((err := edit.addpart(p.p)) != nil)
+ sys->fprint(stderr, "error adding partition: %s\n", err);
+ }
+ }
+}
+
+blankpart(edit: ref Edit)
+{
+ edit.changed = 1;
+}
+
+findmbr(edit: ref Edit)
+{
+ table := gettable(edit.disk, big 0, 1);
+ for(tp := 0; tp < TableSize; tp += TentrySize){
+ p := PCpart.extract(table[tp:], edit.disk);
+ if(p.ptype == TypeDMDDO)
+ mbroffset = big edit.disk.s;
+ }
+}
+
+haveroom(edit: ref Edit, primary: int, start: big): int
+{
+ if(primary) {
+ #
+ # must be open primary slot.
+ # primary slots are taken by primary partitions
+ # and runs of secondary partitions.
+ #
+ n := 0;
+ lastsec := 0;
+ for(i:=0; i<len edit.part; i++) {
+ p := tag2part(edit.part[i]);
+ if(p.primary){
+ n++;
+ lastsec = 0;
+ }else if(!lastsec){
+ n++;
+ lastsec = 1;
+ }
+ }
+ return n<4;
+ }
+
+ #
+ # secondary partitions can be inserted between two primary
+ # partitions only if there is an empty primary slot.
+ # otherwise, we can put a new secondary partition next
+ # to a secondary partition no problem.
+ #
+ n := 0;
+ for(i:=0; i<len edit.part; i++){
+ p := tag2part(edit.part[i]);
+ if(p.primary)
+ n++;
+ pend := p.p.end;
+ q: ref Dospart;
+ qstart: big;
+ if(i+1<len edit.part){
+ q = tag2part(edit.part[i+1]);
+ qstart = q.p.start;
+ }else{
+ qstart = edit.end;
+ q = nil;
+ }
+ if(start < pend || start >= qstart)
+ continue;
+ # we go between these two
+ if(p.primary==0 || (q != nil && q.primary==0))
+ return 1;
+ }
+ # not next to a secondary, need a new primary
+ return n<4;
+}
+
+autopart(edit: ref Edit)
+{
+ for(i:=0; i<len edit.part; i++)
+ if(tag2part(edit.part[i]).pc.ptype == Type9)
+ return;
+
+ # look for the biggest gap in which we can put a primary partition
+ start := big 0;
+ bigsize := big 0;
+ bigstart := big 0;
+ for(i=0; i<len edit.part; i++) {
+ p := tag2part(edit.part[i]);
+ if(p.p.start > start && p.p.start - start > bigsize && haveroom(edit, 1, start)) {
+ bigsize = p.p.start - start;
+ bigstart = start;
+ }
+ start = p.p.end;
+ }
+
+ if(edit.end - start > bigsize && haveroom(edit, 1, start)) {
+ bigsize = edit.end - start;
+ bigstart = start;
+ }
+ if(bigsize < big 1) {
+ sys->fprint(stderr, "couldn't find space or partition slot for plan 9 partition\n");
+ return;
+ }
+
+ # set new partition active only if no others are
+ active := Active;
+ for(i=0; i<len edit.part; i++){
+ p := tag2part(edit.part[i]);
+ if(p.primary && p.pc.active & Active)
+ active = 0;
+ }
+
+ # add new plan 9 partition
+ bigsize *= sec2cyl;
+ bigstart *= sec2cyl;
+ if(bigstart == big 0) {
+ bigstart += big edit.disk.s;
+ bigsize -= big edit.disk.s;
+ }
+ p := mkpart(nil, 1, bigstart, bigsize, nil);
+ p.p.changed = 1;
+ p.pc.active = active;
+ p.pc.ptype = Type9;
+ edit.changed = 1;
+ if((err := edit.addpart(p.p)) != nil){
+ sys->fprint(stderr, "error adding plan9 partition: %s\n", err);
+ return;
+ }
+}
+
+namelist: list of string;
+
+plan9print(part: ref Dospart, fd: ref Sys->FD)
+{
+ vname := types[part.pc.ptype].name;
+ if(vname==nil) {
+ part.p.ctlname = "";
+ return;
+ }
+
+ start := mbroffset+part.lba;
+ end := start+part.size;
+
+ # avoid names like plan90
+ i := len vname - 1;
+ if(isdigit(vname[i]))
+ sep := ".";
+ else
+ sep = "";
+
+ i = 0;
+ name := sys->sprint("%s", vname);
+ ok: int;
+ do {
+ ok = 1;
+ for(nl := namelist; nl != nil; nl = tl nl)
+ if(name == hd nl) {
+ i++;
+ name = sys->sprint("%s%s%d", vname, sep, i);
+ ok = 0;
+ }
+ } while(ok == 0);
+
+ namelist = name :: namelist;
+ part.p.ctlname = name;
+
+ if(fd != nil)
+ sys->print("part %s %bd %bd\n", name, start, end);
+}
+
+cmdprintctl(edit: ref Edit, ctlfd: ref Sys->FD)
+{
+ namelist = nil;
+ for(i:=0; i<len edit.part; i++)
+ plan9print(tag2part(edit.part[i]), nil);
+ edit.ctldiff(ctlfd);
+}
+
+cmdokname(nil: ref Edit, name: string): string
+{
+ if(name[0] != 'p' && name[0] != 's' || len name < 2)
+ return "name must be pN or sN";
+ for(i := 1; i < len name; i++)
+ if(!isdigit(name[i]))
+ return "name must be pN or sN";
+
+ return nil;
+}
+
+KB: con big 1024;
+MB: con KB*KB;
+GB: con KB*MB;
+
+cmdsum(edit: ref Edit, vp: ref Part, a, b: big)
+{
+ if(vp != nil)
+ p := tag2part(vp);
+
+ qual: string;
+ if(p != nil && p.p.changed)
+ qual += "'";
+ else
+ qual += " ";
+ if(p != nil && p.pc.active&Active)
+ qual += "*";
+ else
+ qual += " ";
+
+ if(p != nil)
+ name := p.p.name;
+ else
+ name = "empty";
+ if(p != nil)
+ ty := " "+typestr0(p.pc.ptype);
+ else
+ ty = "";
+
+ sz := (b-a)*big edit.disk.secsize*sec2cyl;
+ suf := "B";
+ div := big 1;
+ if(sz >= big 1*GB){
+ suf = "GB";
+ div = GB;
+ }else if(sz >= big 1*MB){
+ suf = "MB";
+ div = MB;
+ }else if(sz >= big 1*KB){
+ suf = "KB";
+ div = KB;
+ }
+
+ if(div == big 1)
+ sys->print("%s %-12s %*bd %-*bd (%bd cylinders, %bd %s)%s\n", qual, name,
+ edit.disk.width, a, edit.disk.width, b, b-a, sz, suf, ty);
+ else
+ sys->print("%s %-12s %*bd %-*bd (%bd cylinders, %bd.%.2d %s)%s\n", qual, name,
+ edit.disk.width, a, edit.disk.width, b, b-a,
+ sz/div, int(((sz%div)*big 100)/div), suf, ty);
+}
+
+cmdadd(edit: ref Edit, name: string, start: big, end: big): string
+{
+ if(!haveroom(edit, name[0]=='p', start))
+ return "no room for partition";
+ start *= sec2cyl;
+ end *= sec2cyl;
+ if(start == big 0 || name[0] != 'p')
+ start += big edit.disk.s;
+ p := mkpart(name, name[0]=='p', start, end-start, nil);
+ p.p.changed = 1;
+ p.pc.ptype = Type9;
+ return edit.addpart(p.p);
+}
+
+cmddel(edit: ref Edit, p: ref Part): string
+{
+ return edit.delpart(p);
+}
+
+cmdwrite(edit: ref Edit): string
+{
+ wrpart(edit);
+ return nil;
+}
+
+help: con
+ "A name - set partition active\n"+
+ "P - sys->print table in ctl format\n"+
+ "R - restore disk back to initial configuration and exit\n"+
+ "e - show empty dos partitions\n"+
+ "t name [type] - set partition type\n";
+
+cmdhelp(nil: ref Edit): string
+{
+ sys->print("%s\n", help);
+ return nil;
+}
+
+cmdactive(edit: ref Edit, f: array of string): string
+{
+ if(len f != 2)
+ return "args";
+
+ if(f[1][0] != 'p')
+ return "cannot set secondary partition active";
+
+ if((p := tag2part(edit.findpart(f[1]))) == nil)
+ return "unknown partition";
+
+ for(i:=0; i<len edit.part; i++) {
+ ip := tag2part(edit.part[i]);
+ if(ip.pc.active & Active) {
+ ip.pc.active &= ~Active;
+ ip.p.changed = 1;
+ edit.changed = 1;
+ }
+ }
+
+ if((p.pc.active & Active) == 0) {
+ p.pc.active |= Active;
+ p.p.changed = 1;
+ edit.changed = 1;
+ }
+
+ return nil;
+}
+
+strupr(s: string): string
+{
+ for(i := 0; i < len s; i++)
+ if(s[i] >= 'a' && s[i] <= 'z')
+ s[i] += 'A' - 'a';
+ return s;
+}
+
+dumplist()
+{
+ n := 0;
+ for(i:=0; i<len types; i++) {
+ if(types[i].desc != nil) {
+ sys->print("%-16s", types[i].desc);
+ if(n++%4 == 3)
+ sys->print("\n");
+ }
+ }
+ if(n%4)
+ sys->print("\n");
+}
+
+cmdtype(edit: ref Edit, f: array of string): string
+{
+ if(len f < 2)
+ return "args";
+
+ if((p := tag2part(edit.findpart(f[1]))) == nil)
+ return "unknown partition";
+
+ q: string;
+ if(len f == 2) {
+ for(;;) {
+ sys->fprint(stderr, "new partition type [? for list]: ");
+ q = edit.getline();
+ if(q[0] == '?')
+ dumplist();
+ else
+ break;
+ }
+ } else
+ q = f[2];
+
+ q = strupr(q);
+ for(i:=0; i<len types; i++)
+ if(types[i].desc != nil && types[i].desc == q)
+ break;
+ if(i < len types && p.pc.ptype != i) {
+ p.pc.ptype = i;
+ p.p.changed = 1;
+ edit.changed = 1;
+ }
+ return nil;
+}
+
+cmdext(edit: ref Edit, f: array of string): string
+{
+ case f[0][0] {
+ 'A' =>
+ return cmdactive(edit, f);
+ 't' =>
+ return cmdtype(edit, f);
+ 'R' =>
+ recover(edit);
+ return nil;
+ * =>
+ return "unknown command";
+ }
+}
+
+wrextend(edit: ref Edit, i: int, xbase: big, startlba: big): (int, big)
+{
+ if(i == len edit.part){
+ endlba := edit.disk.secs;
+ if(startlba < endlba)
+ wrzerotab(edit.disk, mbroffset+startlba);
+ return (i, endlba);
+ }
+
+ p := tag2part(edit.part[i]);
+ if(p.primary){
+ endlba := p.p.start*sec2cyl;
+ if(startlba < endlba)
+ wrzerotab(edit.disk, mbroffset+startlba);
+ return (i, endlba);
+ }
+
+ disk := edit.disk;
+ table := gettable(disk, mbroffset+startlba, 0);
+
+ (ni, endlba) := wrextend(edit, i+1, xbase, p.p.end*sec2cyl);
+
+ tp := wrtentry(disk, table[0:], p.pc.active, p.pc.ptype, startlba, startlba+big disk.s, p.p.end*sec2cyl);
+ if(p.p.end*sec2cyl != endlba)
+ tp += wrtentry(disk, table[tp:], 0, TypeEXTENDED, xbase, p.p.end*sec2cyl, endlba);
+
+ for(; tp<TableSize; tp++)
+ table[tp] = byte 0;
+
+ table[Omagic] = byte Magic0;
+ table[Omagic+1] = byte Magic1;
+
+ if(puttable(edit.disk, table, mbroffset+startlba) < 0)
+ recover(edit);
+ return (ni, endlba);
+}
+
+wrzerotab(disk: ref Disk, addr: big)
+{
+ table := array[TableSize+2] of {Omagic => byte Magic0, Omagic+1 => byte Magic1, * => byte 0};
+ if(puttable(disk, table, addr) < 0)
+ recover(edit);
+}
+
+wrpart(edit: ref Edit)
+{
+ disk := edit.disk;
+
+ table := gettable(disk, mbroffset, 0);
+
+ tp := 0;
+ for(i:=0; i<len edit.part && tp<TableSize; ) {
+ p := tag2part(edit.part[i]);
+ if(p.p.start == big 0)
+ s := big disk.s;
+ else
+ s = p.p.start*sec2cyl;
+ if(p.primary) {
+ tp += wrtentry(disk, table[tp:], p.pc.active, p.pc.ptype, big 0, s, p.p.end*sec2cyl);
+ i++;
+ }else{
+ (ni, endlba) := wrextend(edit, i, p.p.start*sec2cyl, p.p.start*sec2cyl);
+ if(endlba >= big 1024*sec2cyl)
+ t := TypeEXTHUGE;
+ else
+ t = TypeEXTENDED;
+ tp += wrtentry(disk, table[tp:], 0, t, big 0, s, endlba);
+ i = ni;
+ }
+ }
+ for(; tp<TableSize; tp++)
+ table[tp] = byte 0;
+
+ if(i != len edit.part)
+ raise "wrpart: cannot happen #1";
+
+ if(puttable(disk, table, mbroffset) < 0)
+ recover(edit);
+
+ # bring parts up to date
+ namelist = nil;
+ for(i=0; i<len edit.part; i++)
+ plan9print(tag2part(edit.part[i]), nil);
+
+ if(edit.ctldiff(disk.ctlfd) < 0)
+ sys->fprint(stderr, "?warning: partitions could not be updated in devsd\n");
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+sysfatal(s: string)
+{
+ sys->fprint(stderr, "fdisk: %s\n", s);
+ raise "fail:error";
+}
+
+exits(s: string)
+{
+ if(s != nil)
+ raise "fail:"+s;
+ exit;
+}
+
+assert(i: int)
+{
+ if(!i)
+ raise "assertion failed";
+}
+
+wrtentry(disk: ref Disk, entry: array of byte, active: int, ptype: int, xbase: big, lba: big, end: big): int
+{
+ pc: PCpart;
+ pc.active = active;
+ pc.ptype = ptype;
+ pc.base = xbase;
+ pc.offset = lba-xbase;
+ pc.size = end-lba;
+ entry[0:] = pc.bytes(disk);
+ return TentrySize;
+}