diff options
Diffstat (limited to 'appl/cmd/disk/prep')
| -rw-r--r-- | appl/cmd/disk/prep/calc.tab.b | 454 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/calc.tab.m | 7 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/calc.y | 174 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/fdisk.b | 925 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/mkfile | 26 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/pedit.b | 504 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/pedit.m | 53 | ||||
| -rw-r--r-- | appl/cmd/disk/prep/prep.b | 509 |
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; +} |
