summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/prep/prep.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/disk/prep/prep.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/disk/prep/prep.b')
-rw-r--r--appl/cmd/disk/prep/prep.b509
1 files changed, 509 insertions, 0 deletions
diff --git a/appl/cmd/disk/prep/prep.b b/appl/cmd/disk/prep/prep.b
new file mode 100644
index 00000000..fa4c60a1
--- /dev/null
+++ b/appl/cmd/disk/prep/prep.b
@@ -0,0 +1,509 @@
+implement Prep;
+
+#
+# prepare plan 9/inferno disk partition
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "disks.m";
+ disks: Disks;
+ Disk: import disks;
+ readn: import disks;
+
+include "pedit.m";
+ pedit: Pedit;
+ Edit, Part: import pedit;
+
+include "arg.m";
+
+Prep: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+blank := 0;
+file := 0;
+doauto := 0;
+printflag := 0;
+opart: array of ref Part;
+secbuf: array of byte;
+osecbuf: array of byte;
+zeroes: array of byte;
+rdonly := 0;
+dowrite := 0;
+
+Prepedit: type Edit[string];
+
+edit: ref Edit;
+
+Auto: adt
+{
+ name: string;
+ min: big;
+ max: big;
+ weight: int;
+ alloc: int;
+ size: big;
+};
+
+KB: con big 1024;
+MB: con KB*KB;
+GB: con KB*MB;
+
+#
+# Order matters -- this is the layout order on disk.
+#
+auto: array of Auto = array[] of {
+ ("9fat", big 10*MB, big 100*MB, 10, 0, big 0),
+ ("nvram", big 512, big 512, 1, 0, big 0),
+ ("fscfg", big 512, big 512, 1, 0, big 0),
+ ("fs", big 200*MB, big 0, 10, 0, big 0),
+ ("fossil", big 200*MB, big 0, 4, 0, big 0),
+ ("arenas", big 500*MB, big 0, 20, 0, big 0),
+ ("isect", big 25*MB, big 0, 1, 0, big 0),
+ ("other", big 200*MB, big 0, 4, 0, big 0),
+ ("swap", big 100*MB, big 512*MB, 1, 0, big 0),
+ ("cache", big 50*MB, big 1*GB, 2, 0, big 0),
+};
+
+stderr: ref Sys->FD;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ disks = load Disks Disks->PATH;
+ pedit = load Pedit Pedit->PATH;
+
+ sys->pctl(Sys->FORKFD, nil);
+ disks->init();
+ pedit->init();
+
+ edit = Edit.mk("sector");
+
+ edit.add = cmdadd;
+ edit.del = cmddel;
+ edit.okname = cmdokname;
+ edit.sum = cmdsum;
+ edit.write = cmdwrite;
+
+ stderr = sys->fildes(2);
+ secsize := 0;
+ arg := load Arg Arg->PATH;
+ arg->init(args);
+ arg->setusage("disk/prep [-bfprw] [-a partname]... [-s sectorsize] /dev/sdC0/plan9");
+ while((o := arg->opt()) != 0)
+ case o {
+ 'a' =>
+ p := arg->earg();
+ for(i:=0; i<len auto; i++){
+ if(p == auto[i].name){
+ if(auto[i].alloc){
+ sys->fprint(stderr, "you said -a %s more than once.\n", p);
+ arg->usage();
+ }
+ auto[i].alloc = 1;
+ break;
+ }
+ }
+ if(i == len auto){
+ sys->fprint(stderr, "don't know how to create automatic partition %s\n", p);
+ arg->usage();
+ }
+ doauto = 1;
+ 'b' =>
+ blank++;
+ 'f' =>
+ file++;
+ 'p' =>
+ printflag++;
+ rdonly++;
+ 'r' =>
+ rdonly++;
+ 's' =>
+ secsize = int arg->earg();
+ 'w' =>
+ dowrite++;
+ * =>
+ arg->usage();
+ }
+ args = arg->argv();
+ if(len args != 1)
+ arg->usage();
+ arg = nil;
+
+ mode := Sys->ORDWR;
+ if(rdonly)
+ mode = Sys->OREAD;
+ disk := Disk.open(hd args, mode, file);
+ if(disk == nil) {
+ sys->fprint(stderr, "cannot open disk: %r\n");
+ exits("opendisk");
+ }
+
+ if(secsize != 0) {
+ disk.secsize = secsize;
+ disk.secs = disk.size / big secsize;
+ }
+ edit.end = disk.secs;
+
+ checkfat(disk);
+
+ secbuf = array[disk.secsize+1] of byte;
+ osecbuf = array[disk.secsize+1] of byte;
+ zeroes = array[disk.secsize+1] of {* => byte 0};
+ edit.disk = disk;
+
+ if(blank == 0)
+ rdpart(edit);
+
+ # save old partition table
+ opart = array[len edit.part] of ref Part;
+ opart[0:] = edit.part;
+
+ if(printflag) {
+ edit.runcmd("P");
+ exits(nil);
+ }
+
+ if(doauto)
+ autopart(edit);
+
+ if(dowrite) {
+ edit.runcmd("w");
+ exits(nil);
+ }
+
+ edit.runcmd("p");
+ for(;;) {
+ sys->fprint(stderr, ">>> ");
+ edit.runcmd(edit.getline());
+ }
+}
+
+cmdsum(edit: ref Edit, p: ref Part, a: big, b: big)
+{
+ c := ' ';
+ name := "empty";
+ if(p != nil){
+ if(p.changed)
+ c = '\'';
+ name = p.name;
+ }
+
+ sz := (b-a)*big edit.disk.secsize;
+ 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("%c %-12s %*bd %-*bd (%bd sectors, %bd %s)\n", c, name,
+ edit.disk.width, a, edit.disk.width, b, b-a, sz, suf);
+ else
+ sys->print("%c %-12s %*bd %-*bd (%bd sectors, %bd.%.2d %s)\n", c, name,
+ edit.disk.width, a, edit.disk.width, b, b-a,
+ sz/div, int (((sz%div)*big 100)/div), suf);
+}
+
+cmdadd(edit: ref Edit, name: string, start: big, end: big): string
+{
+ if(start < big 2 && name == "9fat")
+ return "overlaps with the pbs and/or the partition table";
+
+ return edit.addpart(mkpart(name, start, end, 1));
+}
+
+cmddel(edit: ref Edit, p: ref Part): string
+{
+ return edit.delpart(p);
+}
+
+cmdwrite(edit: ref Edit): string
+{
+ wrpart(edit);
+ return nil;
+}
+
+isfrog := array[256] of {
+ byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # NUL
+ byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # BKS
+ byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # DLE
+ byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # CAN
+ ' ' => byte 1,
+ '/' => byte 1,
+ 16r7f=> byte 1,
+ * => byte 0
+};
+
+cmdokname(nil: ref Edit, elem: string): string
+{
+ for(i := 0; i < len elem; i++)
+ if(int isfrog[elem[i]])
+ return "bad character in name";
+ return nil;
+}
+
+mkpart(name: string, start: big, end: big, changed: int): ref Part
+{
+ p := ref Part;
+ p.name = name;
+ p.ctlname = name;
+ p.start = start;
+ p.end = end;
+ p.changed = changed;
+ p.ctlstart = big 0;
+ p.ctlend = big 0;
+ return p;
+}
+
+# plan9 partition table is first sector of the disk
+
+rdpart(edit: ref Edit)
+{
+ disk := edit.disk;
+ sys->seek(disk.fd, big disk.secsize, 0);
+ if(readn(disk.fd, osecbuf, disk.secsize) != disk.secsize)
+ return;
+ osecbuf[disk.secsize] = byte 0;
+ secbuf[0:] = osecbuf;
+
+ for(i := 0; i < disk.secsize; i++)
+ if(secbuf[i] == byte 0)
+ break;
+
+ tab := string secbuf[0:i];
+ if(len tab < 4 || tab[0:4] != "part"){
+ sys->fprint(stderr, "no plan9 partition table found\n");
+ return;
+ }
+
+ waserr := 0;
+ (nline, lines) := sys->tokenize(tab, "\n");
+ for(i=0; i<nline; i++){
+ line := hd lines;
+ lines = tl lines;
+ if(len line < 4 || line[0:4] != "part"){
+ waserr = 1;
+ continue;
+ }
+
+ (nf, f) := sys->tokenize(line, " \t\r");
+ if(nf != 4 || hd f != "part"){
+ waserr = 1;
+ continue;
+ }
+
+ a := big hd tl tl f;
+ b := big hd tl tl tl f;
+ if(a >= b){
+ waserr = 1;
+ continue;
+ }
+
+ if((err := edit.addpart(mkpart(hd tl f, a, b, 0))) != nil) {
+ sys->fprint(stderr, "?%s: not continuing\n", err);
+ exits("partition");
+ }
+ }
+ if(waserr)
+ sys->fprint(stderr, "syntax error reading partition\n");
+}
+
+min(a, b: big): big
+{
+ if(a < b)
+ return a;
+ return b;
+}
+
+autopart(edit: ref Edit)
+{
+ if(len edit.part > 0) {
+ if(doauto)
+ sys->fprint(stderr, "partitions already exist; not repartitioning\n");
+ return;
+ }
+
+ secs := edit.disk.secs;
+ secsize := big edit.disk.secsize;
+ for(;;){
+ # compute total weights
+ totw := 0;
+ for(i:=0; i<len auto; i++){
+ if(auto[i].alloc==0 || auto[i].size != big 0)
+ continue;
+ totw += auto[i].weight;
+ }
+ if(totw == 0)
+ break;
+
+ if(secs <= big 0){
+ sys->fprint(stderr, "ran out of disk space during autopartition.\n");
+ return;
+ }
+
+ # assign any minimums for small disks
+ futz := 0;
+ for(i=0; i<len auto; i++){
+ if(auto[i].alloc==0 || auto[i].size != big 0)
+ continue;
+ s := (secs*big auto[i].weight)/big totw;
+ if(s < big auto[i].min/secsize){
+ auto[i].size = big auto[i].min/secsize;
+ secs -= auto[i].size;
+ futz = 1;
+ break;
+ }
+ }
+ if(futz)
+ continue;
+
+ # assign any maximums for big disks
+ futz = 0;
+ for(i=0; i<len auto; i++){
+ if(auto[i].alloc==0 || auto[i].size != big 0)
+ continue;
+ s := (secs*big auto[i].weight)/big totw;
+ if(auto[i].max != big 0 && s > auto[i].max/secsize){
+ auto[i].size = auto[i].max/secsize;
+ secs -= auto[i].size;
+ futz = 1;
+ break;
+ }
+ }
+ if(futz)
+ continue;
+
+ # finally, assign partition sizes according to weights
+ for(i=0; i<len auto; i++){
+ if(auto[i].alloc==0 || auto[i].size != big 0)
+ continue;
+ s := (secs*big auto[i].weight)/big totw;
+ auto[i].size = s;
+
+ # use entire disk even in face of rounding errors
+ secs -= auto[i].size;
+ totw -= auto[i].weight;
+ }
+ }
+
+ for(i:=0; i<len auto; i++)
+ if(auto[i].alloc)
+ sys->print("%s %bud\n", auto[i].name, auto[i].size);
+
+ s := big 0;
+ for(i=0; i<len auto; i++){
+ if(auto[i].alloc == 0)
+ continue;
+ if((err := edit.addpart(mkpart(auto[i].name, s, s+auto[i].size, 1))) != nil)
+ sys->fprint(stderr, "addpart %s: %s\n", auto[i].name, err);
+ s += auto[i].size;
+ }
+}
+
+restore(edit: ref Edit, ctlfd: ref Sys->FD)
+{
+ offset := edit.disk.offset;
+ sys->fprint(stderr, "attempting to restore partitions to previous state\n");
+ if(sys->seek(edit.disk.wfd, big edit.disk.secsize, 0) != big 0){
+ sys->fprint(stderr, "cannot restore: error seeking on disk: %r\n");
+ exits("inconsistent");
+ }
+
+ if(sys->write(edit.disk.wfd, osecbuf, edit.disk.secsize) != edit.disk.secsize){
+ sys->fprint(stderr, "cannot restore: couldn't write old partition table to disk: %r\n");
+ exits("inconsistent");
+ }
+
+ if(ctlfd != nil){
+ for(i:=0; i<len edit.part; i++)
+ sys->fprint(ctlfd, "delpart %s", edit.part[i].name);
+ for(i=0; i<len opart; i++){
+ if(sys->fprint(ctlfd, "part %s %bd %bd", opart[i].name, opart[i].start+offset, opart[i].end+offset) < 0){
+ sys->fprint(stderr, "restored disk partition table but not kernel table; reboot\n");
+ exits("inconsistent");
+ }
+ }
+ }
+ exits("restored");
+}
+
+wrpart(edit: ref Edit)
+{
+ disk := edit.disk;
+
+ secbuf[0:] = zeroes;
+ n := 0;
+ for(i:=0; i<len edit.part; i++){
+ a := sys->aprint("part %s %bd %bd\n",
+ edit.part[i].name, edit.part[i].start, edit.part[i].end);
+ if(n + len a > disk.secsize){
+ sys->fprint(stderr, "partition table bigger than sector (%d bytes)\n", disk.secsize);
+ exits("overflow");
+ }
+ secbuf[n:] = a;
+ n += len a;
+ }
+
+ if(sys->seek(disk.wfd, big disk.secsize, 0) != big disk.secsize){
+ sys->fprint(stderr, "error seeking to %d on disk: %r\n", disk.secsize);
+ exits("seek");
+ }
+
+ if(sys->write(disk.wfd, secbuf, disk.secsize) != disk.secsize){
+ sys->fprint(stderr, "error writing partition table to disk: %r\n");
+ restore(edit, nil);
+ }
+
+ if(edit.ctldiff(disk.ctlfd) < 0)
+ sys->fprint(stderr, "?warning: partitions could not be updated in devsd\n");
+}
+
+#
+# Look for a boot sector in sector 1, as would be
+# the case if editing /dev/sdC0/data when that
+# was really a bootable disk.
+#
+checkfat(disk: ref Disk)
+{
+ buf := array[32] of byte;
+
+ if(sys->seek(disk.fd, big disk.secsize, 0) != big disk.secsize ||
+ sys->read(disk.fd, buf, len buf) < len buf)
+ return;
+
+ if(buf[0] != byte 16rEB || buf[1] != byte 16r3C || buf[2] != byte 16r90)
+ return;
+
+ sys->fprint(stderr,
+ "there's a fat partition where the\n"+
+ "plan9 partition table would go.\n"+
+ "if you really want to overwrite it, zero\n"+
+ "the second sector of the disk and try again\n");
+
+ exits("fat partition");
+}
+
+exits(s: string)
+{
+ if(s != nil)
+ raise "fail:"+s;
+ exit;
+}