ref: db1eb844461b07a25ca49117851fa874fd88e065
dir: /appl/cmd/disk/prep/prep.b/
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; 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(sys->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; }