summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/prep
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/disk/prep')
-rw-r--r--appl/cmd/disk/prep/calc.tab.b454
-rw-r--r--appl/cmd/disk/prep/calc.tab.m7
-rw-r--r--appl/cmd/disk/prep/calc.y174
-rw-r--r--appl/cmd/disk/prep/fdisk.b925
-rw-r--r--appl/cmd/disk/prep/mkfile26
-rw-r--r--appl/cmd/disk/prep/pedit.b504
-rw-r--r--appl/cmd/disk/prep/pedit.m53
-rw-r--r--appl/cmd/disk/prep/prep.b509
8 files changed, 2652 insertions, 0 deletions
diff --git a/appl/cmd/disk/prep/calc.tab.b b/appl/cmd/disk/prep/calc.tab.b
new file mode 100644
index 00000000..25f81487
--- /dev/null
+++ b/appl/cmd/disk/prep/calc.tab.b
@@ -0,0 +1,454 @@
+implement Calc;
+
+#line 2 "calc.y"
+#
+# from Plan 9. subject to the Lucent Public License 1.02
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+ NUM,
+ DOT,
+ DOLLAR,
+ ADD,
+ SUB,
+ MUL,
+ DIV,
+ FRAC,
+ NEG: con iota;
+
+Exp: adt {
+ ty: int;
+ n: big;
+ e1, e2: cyclic ref Exp;
+};
+
+YYSTYPE: adt {
+ e: ref Exp;
+};
+yyexp: ref Exp;
+
+YYLEX: adt {
+ s: string;
+ n: int;
+ lval: YYSTYPE;
+ lex: fn(l: self ref YYLEX): int;
+ error: fn(l: self ref YYLEX, msg: string);
+};
+Calc: module {
+
+ parseexpr: fn(s: string, a, b, c: big): (big, string);
+ init: fn(nil: ref Draw->Context, nil: list of string);
+NUMBER: con 57346;
+UNARYMINUS: con 57347;
+
+};
+YYEOFCODE: con 1;
+YYERRCODE: con 2;
+YYMAXDEPTH: con 200;
+
+#line 68 "calc.y"
+
+
+mkNUM(x: big): ref Exp
+{
+ return ref Exp(NUM, x, nil, nil);
+}
+
+mkOP(ty: int, e1: ref Exp, e2: ref Exp): ref Exp
+{
+ return ref Exp(ty, big 0, e1, e2);
+}
+
+dot, size, dollar: big;
+
+YYLEX.lex(l: self ref YYLEX): int
+{
+ while(l.n < len l.s && isspace(l.s[l.n]))
+ l.n++;
+
+ if(l.n == len l.s)
+ return -1;
+
+ if(isdigit(l.s[l.n])){
+ for(o := l.n; o < len l.s && isdigit(l.s[o]); o++)
+ ;
+ l.lval.e = mkNUM(big l.s[l.n:o]);
+ l.n = o;
+ return NUMBER;
+ }
+
+ return l.s[l.n++];
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
+}
+
+YYLEX.error(nil: self ref YYLEX, s: string)
+{
+ raise s;
+}
+
+eval(e: ref Exp): big
+{
+ case e.ty {
+ NUM =>
+ return e.n;
+ DOT =>
+ return dot;
+ DOLLAR =>
+ return dollar;
+ ADD =>
+ return eval(e.e1)+eval(e.e2);
+ SUB =>
+ return eval(e.e1)-eval(e.e2);
+ MUL =>
+ return eval(e.e1)*eval(e.e2);
+ DIV =>
+ i := eval(e.e2);
+ if(i == big 0)
+ raise "division by zero";
+ return eval(e.e1)/i;
+ FRAC =>
+ return (size*eval(e.e1))/big 100;
+ NEG =>
+ return -eval(e.e1);
+ * =>
+ raise "invalid operator";
+ }
+}
+
+parseexpr(s: string, xdot: big, xdollar: big, xsize: big): (big, string)
+{
+ dot = xdot;
+ size = xsize;
+ dollar = xdollar;
+ l := ref YYLEX(s, 0, YYSTYPE(nil));
+ {
+ yyparse(l);
+ if(yyexp == nil)
+ return (big 0, "nil yylval?");
+ return (eval(yyexp), nil);
+ }exception e{
+ "*" =>
+ return (big 0, e);
+ }
+}
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+
+ while((args = tl args) != nil){
+ (r, e) := parseexpr(hd args, big 1000, big 1000000, big 1000000);
+ if(e != nil)
+ sys->print("%s\n", e);
+ else
+ sys->print("%bd\n", r);
+ }
+}
+
+yyexca := array[] of {-1, 1,
+ 1, -1,
+ -2, 0,
+};
+YYNPROD: con 12;
+YYPRIVATE: con 57344;
+yytoknames: array of string;
+yystates: array of string;
+yydebug: con 0;
+YYLAST: con 30;
+yyact := array[] of {
+ 8, 9, 10, 11, 3, 12, 7, 2, 12, 19,
+ 1, 4, 5, 6, 13, 14, 15, 16, 17, 18,
+ 8, 9, 10, 11, 0, 12, 10, 11, 0, 12,
+};
+yypact := array[] of {
+ 0,-1000, 15,-1000,-1000,-1000, 0, 0, 0, 0,
+ 0, 0,-1000, -5,-1000, 19, 19, -2, -2,-1000,
+};
+yypgo := array[] of {
+ 0, 7, 10,
+};
+yyr1 := array[] of {
+ 0, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1,
+};
+yyr2 := array[] of {
+ 0, 1, 1, 1, 1, 3, 3, 3, 3, 3,
+ 2, 2,
+};
+yychk := array[] of {
+-1000, -2, -1, 4, 11, 12, 13, 6, 5, 6,
+ 7, 8, 10, -1, -1, -1, -1, -1, -1, 14,
+};
+yydef := array[] of {
+ 0, -2, 1, 2, 3, 4, 0, 0, 0, 0,
+ 0, 0, 10, 0, 11, 6, 7, 8, 9, 5,
+};
+yytok1 := array[] of {
+ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 12, 10, 3, 3,
+ 13, 14, 7, 5, 3, 6, 11, 8,
+};
+yytok2 := array[] of {
+ 2, 3, 4, 9,
+};
+yytok3 := array[] of {
+ 0
+};
+
+YYSys: module
+{
+ FD: adt
+ {
+ fd: int;
+ };
+ fildes: fn(fd: int): ref FD;
+ fprint: fn(fd: ref FD, s: string, *): int;
+};
+
+yysys: YYSys;
+yystderr: ref YYSys->FD;
+
+YYFLAG: con -1000;
+
+# parser for yacc output
+
+yytokname(yyc: int): string
+{
+ if(yyc > 0 && yyc <= len yytoknames && yytoknames[yyc-1] != nil)
+ return yytoknames[yyc-1];
+ return "<"+string yyc+">";
+}
+
+yystatname(yys: int): string
+{
+ if(yys >= 0 && yys < len yystates && yystates[yys] != nil)
+ return yystates[yys];
+ return "<"+string yys+">\n";
+}
+
+yylex1(yylex: ref YYLEX): int
+{
+ c : int;
+ yychar := yylex.lex();
+ if(yychar <= 0)
+ c = yytok1[0];
+ else if(yychar < len yytok1)
+ c = yytok1[yychar];
+ else if(yychar >= YYPRIVATE && yychar < YYPRIVATE+len yytok2)
+ c = yytok2[yychar-YYPRIVATE];
+ else{
+ n := len yytok3;
+ c = 0;
+ for(i := 0; i < n; i+=2) {
+ if(yytok3[i+0] == yychar) {
+ c = yytok3[i+1];
+ break;
+ }
+ }
+ if(c == 0)
+ c = yytok2[1]; # unknown char
+ }
+ if(yydebug >= 3)
+ yysys->fprint(yystderr, "lex %.4ux %s\n", yychar, yytokname(c));
+ return c;
+}
+
+YYS: adt
+{
+ yyv: YYSTYPE;
+ yys: int;
+};
+
+yyparse(yylex: ref YYLEX): int
+{
+ if(yydebug >= 1 && yysys == nil) {
+ yysys = load YYSys "$Sys";
+ yystderr = yysys->fildes(2);
+ }
+
+ yys := array[YYMAXDEPTH] of YYS;
+
+ yyval: YYSTYPE;
+ yystate := 0;
+ yychar := -1;
+ yynerrs := 0; # number of errors
+ yyerrflag := 0; # error recovery flag
+ yyp := -1;
+ yyn := 0;
+
+yystack:
+ for(;;){
+ # put a state and value onto the stack
+ if(yydebug >= 4)
+ yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= len yys)
+ yys = (array[len yys * 2] of YYS)[0:] = yys;
+ yys[yyp].yys = yystate;
+ yys[yyp].yyv = yyval;
+
+ for(;;){
+ yyn = yypact[yystate];
+ if(yyn > YYFLAG) { # simple state
+ if(yychar < 0)
+ yychar = yylex1(yylex);
+ yyn += yychar;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { # valid shift
+ yychar = -1;
+ yyp++;
+ if(yyp >= len yys)
+ yys = (array[len yys * 2] of YYS)[0:] = yys;
+ yystate = yyn;
+ yys[yyp].yys = yystate;
+ yys[yyp].yyv = yylex.lval;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ if(yydebug >= 4)
+ yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
+ continue;
+ }
+ }
+ }
+
+ # default state action
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+ yychar = yylex1(yylex);
+
+ # look through exception table
+ for(yyxi:=0;; yyxi+=2)
+ if(yyexca[yyxi] == -1 && yyexca[yyxi+1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyexca[yyxi];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyexca[yyxi+1];
+ if(yyn < 0){
+ yyn = 0;
+ break yystack;
+ }
+ }
+
+ if(yyn != 0)
+ break;
+
+ # error ... attempt to resume parsing
+ if(yyerrflag == 0) { # brand new error
+ yylex.error("syntax error");
+ yynerrs++;
+ if(yydebug >= 1) {
+ yysys->fprint(yystderr, "%s", yystatname(yystate));
+ yysys->fprint(yystderr, "saw %s\n", yytokname(yychar));
+ }
+ }
+
+ if(yyerrflag != 3) { # incompletely recovered error ... try again
+ yyerrflag = 3;
+
+ # find a state where "error" is a legal shift action
+ while(yyp >= 0) {
+ yyn = yypact[yys[yyp].yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; # simulate a shift of "error"
+ if(yychk[yystate] == YYERRCODE)
+ continue yystack;
+ }
+
+ # the current yyp has no shift onn "error", pop stack
+ if(yydebug >= 2)
+ yysys->fprint(yystderr, "error recovery pops state %d, uncovers %d\n",
+ yys[yyp].yys, yys[yyp-1].yys );
+ yyp--;
+ }
+ # there is no state on the stack with an error shift ... abort
+ yyn = 1;
+ break yystack;
+ }
+
+ # no shift yet; clobber input char
+ if(yydebug >= 2)
+ yysys->fprint(yystderr, "error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE) {
+ yyn = 1;
+ break yystack;
+ }
+ yychar = -1;
+ # try again in the same state
+ }
+
+ # reduction by production yyn
+ if(yydebug >= 2)
+ yysys->fprint(yystderr, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt := yyp;
+ yyp -= yyr2[yyn];
+# yyval = yys[yyp+1].yyv;
+ yym := yyn;
+
+ # consult goto table to find next state
+ yyn = yyr1[yyn];
+ yyg := yypgo[yyn];
+ yyj := yyg + yys[yyp].yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ case yym {
+
+1=>
+#line 54 "calc.y"
+{ yyexp = yys[yypt-0].yyv.e; return 0; }
+2=>
+yyval.e = yys[yyp+1].yyv.e;
+3=>
+#line 57 "calc.y"
+{ yyval.e = mkOP(DOT, nil, nil); }
+4=>
+#line 58 "calc.y"
+{ yyval.e = mkOP(DOLLAR, nil, nil); }
+5=>
+#line 59 "calc.y"
+{ yyval.e = yys[yypt-1].yyv.e; }
+6=>
+#line 60 "calc.y"
+{ yyval.e = mkOP(ADD, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
+7=>
+#line 61 "calc.y"
+{ yyval.e = mkOP(SUB, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
+8=>
+#line 62 "calc.y"
+{ yyval.e = mkOP(MUL, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
+9=>
+#line 63 "calc.y"
+{ yyval.e = mkOP(DIV, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
+10=>
+#line 64 "calc.y"
+{ yyval.e = mkOP(FRAC, yys[yypt-1].yyv.e, nil); }
+11=>
+#line 65 "calc.y"
+{ yyval.e = mkOP(NEG, yys[yypt-0].yyv.e, nil); }
+ }
+ }
+
+ return yyn;
+}
diff --git a/appl/cmd/disk/prep/calc.tab.m b/appl/cmd/disk/prep/calc.tab.m
new file mode 100644
index 00000000..fa531c74
--- /dev/null
+++ b/appl/cmd/disk/prep/calc.tab.m
@@ -0,0 +1,7 @@
+Calc: module {
+
+ parseexpr: fn(s: string, a, b, c: big): (big, string);
+ init: fn(nil: ref Draw->Context, nil: list of string);
+NUMBER: con 57346;
+UNARYMINUS: con 57347;
+};
diff --git a/appl/cmd/disk/prep/calc.y b/appl/cmd/disk/prep/calc.y
new file mode 100644
index 00000000..7ce56049
--- /dev/null
+++ b/appl/cmd/disk/prep/calc.y
@@ -0,0 +1,174 @@
+%{
+#
+# from Plan 9. subject to the Lucent Public License 1.02
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+ NUM,
+ DOT,
+ DOLLAR,
+ ADD,
+ SUB,
+ MUL,
+ DIV,
+ FRAC,
+ NEG: con iota;
+
+Exp: adt {
+ ty: int;
+ n: big;
+ e1, e2: cyclic ref Exp;
+};
+
+YYSTYPE: adt {
+ e: ref Exp;
+};
+yyexp: ref Exp;
+
+YYLEX: adt {
+ s: string;
+ n: int;
+ lval: YYSTYPE;
+ lex: fn(l: self ref YYLEX): int;
+ error: fn(l: self ref YYLEX, msg: string);
+};
+%}
+%module Calc
+{
+ parseexpr: fn(s: string, a, b, c: big): (big, string);
+ init: fn(nil: ref Draw->Context, nil: list of string);
+}
+
+%token <e> NUMBER
+
+%type <e> expr
+
+%left '+' '-'
+%left '*' '/'
+%left UNARYMINUS '%'
+%%
+top: expr { yyexp = $1; return 0; }
+
+expr: NUMBER
+ | '.' { $$ = mkOP(DOT, nil, nil); }
+ | '$' { $$ = mkOP(DOLLAR, nil, nil); }
+ | '(' expr ')' { $$ = $2; }
+ | expr '+' expr { $$ = mkOP(ADD, $1, $3); }
+ | expr '-' expr { $$ = mkOP(SUB, $1, $3); }
+ | expr '*' expr { $$ = mkOP(MUL, $1, $3); }
+ | expr '/' expr { $$ = mkOP(DIV, $1, $3); }
+ | expr '%' { $$ = mkOP(FRAC, $1, nil); }
+ | '-' expr %prec UNARYMINUS { $$ = mkOP(NEG, $2, nil); }
+ ;
+
+%%
+
+mkNUM(x: big): ref Exp
+{
+ return ref Exp(NUM, x, nil, nil);
+}
+
+mkOP(ty: int, e1: ref Exp, e2: ref Exp): ref Exp
+{
+ return ref Exp(ty, big 0, e1, e2);
+}
+
+dot, size, dollar: big;
+
+YYLEX.lex(l: self ref YYLEX): int
+{
+ while(l.n < len l.s && isspace(l.s[l.n]))
+ l.n++;
+
+ if(l.n == len l.s)
+ return -1;
+
+ if(isdigit(l.s[l.n])){
+ for(o := l.n; o < len l.s && isdigit(l.s[o]); o++)
+ ;
+ l.lval.e = mkNUM(big l.s[l.n:o]);
+ l.n = o;
+ return NUMBER;
+ }
+
+ return l.s[l.n++];
+}
+
+isdigit(c: int): int
+{
+ return c >= '0' && c <= '9';
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
+}
+
+YYLEX.error(nil: self ref YYLEX, s: string)
+{
+ raise s;
+}
+
+eval(e: ref Exp): big
+{
+ case e.ty {
+ NUM =>
+ return e.n;
+ DOT =>
+ return dot;
+ DOLLAR =>
+ return dollar;
+ ADD =>
+ return eval(e.e1)+eval(e.e2);
+ SUB =>
+ return eval(e.e1)-eval(e.e2);
+ MUL =>
+ return eval(e.e1)*eval(e.e2);
+ DIV =>
+ i := eval(e.e2);
+ if(i == big 0)
+ raise "division by zero";
+ return eval(e.e1)/i;
+ FRAC =>
+ return (size*eval(e.e1))/big 100;
+ NEG =>
+ return -eval(e.e1);
+ * =>
+ raise "invalid operator";
+ }
+}
+
+parseexpr(s: string, xdot: big, xdollar: big, xsize: big): (big, string)
+{
+ dot = xdot;
+ size = xsize;
+ dollar = xdollar;
+ l := ref YYLEX(s, 0, YYSTYPE(nil));
+ {
+ yyparse(l);
+ if(yyexp == nil)
+ return (big 0, "nil yylval?");
+ return (eval(yyexp), nil);
+ }exception e{
+ "*" =>
+ return (big 0, e);
+ }
+}
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+
+ while((args = tl args) != nil){
+ (r, e) := parseexpr(hd args, big 1000, big 1000000, big 1000000);
+ if(e != nil)
+ sys->print("%s\n", e);
+ else
+ sys->print("%bd\n", r);
+ }
+}
+
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;
+}
diff --git a/appl/cmd/disk/prep/mkfile b/appl/cmd/disk/prep/mkfile
new file mode 100644
index 00000000..714c26f8
--- /dev/null
+++ b/appl/cmd/disk/prep/mkfile
@@ -0,0 +1,26 @@
+<../../../../mkconfig
+
+TARG=\
+ fdisk.dis\
+ pedit.dis\
+ prep.dis\
+ calc.tab.dis\
+
+MODULES=\
+ pedit.m\
+
+SYSMODULES=\
+ arg.m\
+ sys.m\
+ draw.m\
+ disks.m\
+ bufio.m\
+ string.m\
+
+DISBIN=$ROOT/dis/disk
+
+<$ROOT/mkfiles/mkdis
+
+# calc
+calc.tab.b:
+ yacc -s calc -d calc.y
diff --git a/appl/cmd/disk/prep/pedit.b b/appl/cmd/disk/prep/pedit.b
new file mode 100644
index 00000000..f55bcaff
--- /dev/null
+++ b/appl/cmd/disk/prep/pedit.b
@@ -0,0 +1,504 @@
+implement Pedit;
+
+#
+# disk partition editor
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "disks.m";
+ disks: Disks;
+ Disk: import disks;
+ readn: import disks;
+
+include "draw.m";
+include "calc.tab.m";
+ calc: Calc;
+
+include "pedit.m";
+
+Cmd: adt {
+ c: int;
+ f: ref fn(e: ref Edit, a: array of string): string;
+};
+
+cmds: array of Cmd;
+
+bin: ref Iobuf;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ calc = load Calc "/dis/disk/calc.tab.dis";
+ bufio = load Bufio Bufio->PATH;
+ disks = load Disks Disks->PATH;
+ disks->init();
+
+ bin = bufio->fopen(sys->fildes(0), Bufio->OREAD);
+ cmds = array[] of {
+ ('.', editdot),
+ ('a', editadd),
+ ('d', editdel),
+ ('?', edithelp),
+ ('h', edithelp),
+ ('P', editctlprint),
+ ('p', editprint),
+ ('w', editwrite),
+ ('q', editquit),
+ };
+}
+
+Edit.mk(unit: string): ref Edit
+{
+ e := ref Edit;
+ e.unit = unit;
+ e.dot = big 0;
+ e.end = big 0;
+ e.changed = 0;
+ e.warned = 0;
+ e.lastcmd = 0;
+ return e;
+}
+
+Edit.getline(edit: self ref Edit): string
+{
+ p := bin.gets('\n');
+ if(p == nil){
+ if(edit.changed)
+ sys->fprint(sys->fildes(2), "?warning: changes not written\n");
+ exit;
+ }
+ for(i := 0; i < len p; i++)
+ if(!isspace(p[i]))
+ break;
+ if(i)
+ return p[i:];
+ return p;
+}
+
+Edit.findpart(edit: self ref Edit, name: string): ref Part
+{
+ for(i:=0; i<len edit.part; i++)
+ if(edit.part[i].name == name)
+ return edit.part[i];
+ return nil;
+}
+
+okname(edit: ref Edit, name: string): string
+{
+ if(name[0] == '\0')
+ return "partition has no name";
+
+ for(i:=0; i<len edit.part; i++) {
+ if(name == edit.part[i].name)
+ return sys->sprint("already have partition with name '%s'", name);
+ }
+ return nil;
+}
+
+Edit.addpart(edit: self ref Edit, p: ref Part): string
+{
+ if((err := okname(edit, p.name)) != nil)
+ return err;
+
+ for(i:=0; i<len edit.part; i++) {
+ if(p.start < edit.part[i].end && edit.part[i].start < p.end) {
+ msg := sys->sprint("\"%s\" %bd-%bd overlaps with \"%s\" %bd-%bd",
+ p.name, p.start, p.end,
+ edit.part[i].name, edit.part[i].start, edit.part[i].end);
+ # return msg;
+ }
+ }
+
+ if(len edit.part >= Maxpart)
+ return "too many partitions";
+
+ pa := array[i+1] of ref Part;
+ pa[0:] = edit.part;
+ edit.part = pa;
+
+ edit.part[i] = p;
+ for(; i > 0 && p.start < edit.part[i-1].start; i--) {
+ edit.part[i] = edit.part[i-1];
+ edit.part[i-1] = p;
+ }
+
+ if(p.changed)
+ edit.changed = 1;
+ return nil;
+}
+
+Edit.delpart(edit: self ref Edit, p: ref Part): string
+{
+ n := len edit.part;
+ for(i:=0; i<n; i++)
+ if(edit.part[i] == p)
+ break;
+ if(i >= n)
+ raise "internal error: Part not found";
+ n--;
+ pa := array[n] of ref Part;
+ if(n){
+ pa[0:] = edit.part[0:i];
+ if(i != n)
+ pa[i:] = edit.part[i+1:];
+ }
+ edit.part = pa;
+ edit.changed = 1;
+ return nil;
+}
+
+editdot(edit: ref Edit, argv: array of string): string
+{
+ if(len argv == 1) {
+ sys->print("\t. %bd\n", edit.dot);
+ return nil;
+ }
+
+ if(len argv > 2)
+ return "args";
+
+ (ndot, err) := calc->parseexpr(argv[1], edit.dot, edit.end, edit.end);
+ if(err != nil)
+ return err;
+
+ edit.dot = ndot;
+ return nil;
+}
+
+editadd(edit: ref Edit, argv: array of string): string
+{
+ if(len argv < 2)
+ return "args";
+
+ name := argv[1];
+ if((err := okname(edit, name)) != nil || edit.okname != nil && (err = edit.okname(edit, name)) != nil)
+ return err;
+
+ if(len argv >= 3)
+ q := argv[2];
+ else {
+ sys->fprint(sys->fildes(2), "start %s: ", edit.unit);
+ q = edit.getline();
+ }
+ start: big;
+ (start, err) = calc->parseexpr(q, edit.dot, edit.end, edit.end);
+ if(err != nil)
+ return err;
+
+ if(start < big 0 || start >= edit.end)
+ return "start out of range";
+
+ for(i:=0; i < len edit.part; i++) {
+ if(edit.part[i].start <= start && start < edit.part[i].end)
+ return sys->sprint("start %s in partition '%s'", edit.unit, edit.part[i].name);
+ }
+
+ maxend := edit.end;
+ for(i=0; i < len edit.part; i++)
+ if(start < edit.part[i].start && edit.part[i].start < maxend)
+ maxend = edit.part[i].start;
+
+ if(len argv >= 4)
+ q = argv[3];
+ else {
+ sys->fprint(sys->fildes(2), "end [%bd..%bd] ", start, maxend);
+ q = edit.getline();
+ }
+ end: big;
+ (end, err) = calc->parseexpr(q, edit.dot, maxend, edit.end);
+ if(err != nil)
+ return err;
+
+ if(start == end)
+ return "size zero partition";
+
+ if(end <= start || end > maxend)
+ return "end out of range";
+
+ if(len argv > 4)
+ return "args";
+
+ if((err = edit.add(edit, name, start, end)) != nil)
+ return err;
+
+ edit.dot = end;
+ return nil;
+}
+
+editdel(edit: ref Edit, argv: array of string): string
+{
+ if(len argv != 2)
+ return "args";
+
+ if((p := edit.findpart(argv[1])) == nil)
+ return "no such partition";
+
+ return edit.del(edit, p);
+}
+
+helptext :=
+ ". [newdot] - display or set value of dot\n"+
+ "a name [start [end]] - add partition\n"+
+ "d name - delete partition\n"+
+ "h - sys->print help message\n"+
+ "p - sys->print partition table\n"+
+ "P - sys->print commands to update sd(3) device\n"+
+ "w - write partition table\n"+
+ "q - quit\n";
+
+edithelp(edit: ref Edit, nil: array of string): string
+{
+ sys->print("%s", helptext);
+ if(edit.help != nil)
+ return edit.help(edit);
+ return nil;
+}
+
+editprint(edit: ref Edit, argv: array of string): string
+{
+ if(len argv != 1)
+ return "args";
+
+ lastend := big 0;
+ part := edit.part;
+ for(i:=0; i<len edit.part; i++) {
+ if(lastend < part[i].start)
+ edit.sum(edit, nil, lastend, part[i].start);
+ edit.sum(edit, part[i], part[i].start, part[i].end);
+ lastend = part[i].end;
+ }
+ if(lastend < edit.end)
+ edit.sum(edit, nil, lastend, edit.end);
+ return nil;
+}
+
+editwrite(edit: ref Edit, argv: array of string): string
+{
+ if(len argv != 1)
+ return "args";
+
+ if(edit.disk.rdonly)
+ return "read only";
+
+ err := edit.write(edit);
+ if(err != nil)
+ return err;
+ for(i:=0; i<len edit.part; i++)
+ edit.part[i].changed = 0;
+ edit.changed = 0;
+ return nil;
+}
+
+editquit(edit: ref Edit, argv: array of string): string
+{
+ if(len argv != 1) {
+ edit.warned = 0;
+ return "args";
+ }
+
+ if(edit.changed && (!edit.warned || edit.lastcmd != 'q')) {
+ edit.warned = 1;
+ return "changes unwritten";
+ }
+
+ exit;
+}
+
+editctlprint(edit: ref Edit, argv: array of string): string
+{
+ if(len argv != 1)
+ return "args";
+
+ if(edit.printctl != nil)
+ edit.printctl(edit, sys->fildes(1));
+ else
+ edit.ctldiff(sys->fildes(1));
+ return nil;
+}
+
+Edit.runcmd(edit: self ref Edit, cmd: string)
+{
+ (nf, fl) := sys->tokenize(cmd, " \t\n\r");
+ if(nf < 1)
+ return;
+ f := array[nf] of string;
+ for(nf = 0; fl != nil; fl = tl fl)
+ f[nf++] = hd fl;
+ if(len f[0] != 1) {
+ sys->fprint(sys->fildes(2), "?\n");
+ return;
+ }
+
+ err := "";
+ for(i:=0; i<len cmds; i++) {
+ if(cmds[i].c == f[0][0]) {
+ op := cmds[i].f;
+ err = op(edit, f);
+ break;
+ }
+ }
+ if(i == len cmds){
+ if(edit.ext != nil)
+ err = edit.ext(edit, f);
+ else
+ err = "unknown command";
+ }
+ if(err != nil)
+ sys->fprint(sys->fildes(2), "?%s\n", err);
+ edit.lastcmd = f[0][0];
+}
+
+isspace(c: int): int
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+ctlmkpart(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.ctlstart = big 0;
+ p.ctlend = big 0;
+ p.changed = changed;
+ return p;
+}
+
+rdctlpart(edit: ref Edit)
+{
+ disk := edit.disk;
+ edit.ctlpart = array[0] of ref Part;
+ sys->seek(disk.ctlfd, big 0, 0);
+ buf := array[4096] of byte;
+ if(readn(disk.ctlfd, buf, len buf) <= 0)
+ return;
+ for(i := 0; i < len buf; i++)
+ if(buf[i] == byte 0)
+ break;
+
+ (nline, lines) := sys->tokenize(string buf[0:i], "\n\r");
+ edit.ctlpart = array[nline] of ref Part; # upper bound
+ npart := 0;
+ for(i=0; i<nline; i++){
+ line := hd lines;
+ lines = tl lines;
+ if(len line < 5 || line[0:5] != "part ")
+ continue;
+
+ (nf, f) := sys->tokenize(line, " \t");
+ if(nf != 4 || hd f != "part")
+ break;
+
+ a := big hd tl tl f;
+ b := big hd tl tl tl f;
+
+ if(a >= b)
+ break;
+
+ # only gather partitions contained in the disk partition we are editing
+ if(a < disk.offset || disk.offset+disk.secs < b)
+ continue;
+
+ a -= disk.offset;
+ b -= disk.offset;
+
+ # the partition we are editing does not count
+ if(hd tl f == disk.part)
+ continue;
+
+ edit.ctlpart[npart++] = ctlmkpart(hd tl f, a, b, 0);
+ }
+ if(npart != len edit.ctlpart)
+ edit.ctlpart = edit.ctlpart[0:npart];
+}
+
+ctlstart(p: ref Part): big
+{
+ if(p.ctlstart != big 0)
+ return p.ctlstart;
+ return p.start;
+}
+
+ctlend(p: ref Part): big
+{
+ if(p.ctlend != big 0)
+ return p.ctlend;
+ return p.end;
+}
+
+areequiv(p: ref Part, q: ref Part): int
+{
+ if(p.ctlname == nil || q.ctlname == nil)
+ return 0;
+ return p.ctlname == q.ctlname &&
+ ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q);
+}
+
+unchange(edit: ref Edit, p: ref Part)
+{
+ for(i:=0; i<len edit.ctlpart; i++) {
+ q := edit.ctlpart[i];
+ if(p.start <= q.start && q.end <= p.end)
+ q.changed = 0;
+ }
+ if(p.changed)
+ raise "internal error: Part unchanged";
+}
+
+Edit.ctldiff(edit: self ref Edit, ctlfd: ref Sys->FD): int
+{
+ rdctlpart(edit);
+
+ # everything is bogus until we prove otherwise
+ for(i:=0; i<len edit.ctlpart; i++)
+ edit.ctlpart[i].changed = 1;
+
+ #
+ # partitions with same info have not changed,
+ # and neither have partitions inside them.
+ #
+ for(i=0; i<len edit.ctlpart; i++)
+ for(j:=0; j<len edit.part; j++)
+ if(areequiv(edit.ctlpart[i], edit.part[j])) {
+ unchange(edit, edit.ctlpart[i]);
+ break;
+ }
+
+ waserr := 0;
+ #
+ # delete all the changed partitions except data (we'll add them back if necessary)
+ #
+ for(i=0; i<len edit.ctlpart; i++) {
+ p := edit.ctlpart[i];
+ if(p.changed)
+ if(sys->fprint(ctlfd, "delpart %s\n", p.ctlname)<0) {
+ sys->fprint(sys->fildes(2), "delpart failed: %s: %r\n", p.ctlname);
+ waserr = -1;
+ }
+ }
+
+ #
+ # add all the partitions from the real list;
+ # this is okay since adding a partition with
+ # information identical to what is there is a no-op.
+ #
+ offset := edit.disk.offset;
+ for(i=0; i<len edit.part; i++) {
+ p := edit.part[i];
+ if(p.ctlname != nil) {
+ if(sys->fprint(ctlfd, "part %s %bd %bd\n", p.ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) {
+ sys->fprint(sys->fildes(2), "adding part failed: %s: %r\n", p.ctlname);
+ waserr = -1;
+ }
+ }
+ }
+ return waserr;
+}
diff --git a/appl/cmd/disk/prep/pedit.m b/appl/cmd/disk/prep/pedit.m
new file mode 100644
index 00000000..2b0d142d
--- /dev/null
+++ b/appl/cmd/disk/prep/pedit.m
@@ -0,0 +1,53 @@
+Pedit: module
+{
+ PATH: con "/dis/disk/pedit.dis";
+
+ Part: adt {
+ name: string;
+ ctlname: string;
+ start: big;
+ end: big;
+ ctlstart: big;
+ ctlend: big;
+ changed: int;
+ tag: int;
+ };
+
+ Maxpart: con 32;
+
+ Edit: adt {
+ disk: ref Disks->Disk;
+
+ ctlpart: array of ref Part;
+ part: array of ref Part;
+
+ # to do: replace by channels
+ add: ref fn(e: ref Edit, s: string, a, b: big): string;
+ del: ref fn(e: ref Edit, p: ref Part): string;
+ ext: ref fn(e: ref Edit, f: array of string): string;
+ help: ref fn(e: ref Edit): string;
+ okname: ref fn(e: ref Edit, s: string): string;
+ sum: ref fn(e: ref Edit, p: ref Part, a, b: big);
+ write: ref fn(e: ref Edit): string;
+ printctl: ref fn(e: ref Edit, x: ref Sys->FD);
+
+ unit: string;
+ dot: big;
+ end: big;
+
+ # do not use fields below this line
+ changed: int;
+ warned: int;
+ lastcmd: int;
+
+ mk: fn(unit: string): ref Edit;
+ getline: fn(e: self ref Edit): string;
+ runcmd: fn(e: self ref Edit, c: string);
+ findpart: fn(e: self ref Edit, n: string): ref Part;
+ addpart: fn(e: self ref Edit, p: ref Part): string;
+ delpart: fn(e: self ref Edit, p: ref Part): string;
+ ctldiff: fn(e: self ref Edit, ctlfd: ref Sys->FD): int;
+ };
+
+ init: fn();
+};
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;
+}