1implement Prep; 2 3# 4# prepare plan 9/inferno disk partition 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "bufio.m"; 13 bufio: Bufio; 14 Iobuf: import bufio; 15 16include "disks.m"; 17 disks: Disks; 18 Disk: import disks; 19 20include "pedit.m"; 21 pedit: Pedit; 22 Edit, Part: import pedit; 23 24include "arg.m"; 25 26Prep: module 27{ 28 init: fn(nil: ref Draw->Context, nil: list of string); 29}; 30 31blank := 0; 32file := 0; 33doauto := 0; 34printflag := 0; 35opart: array of ref Part; 36secbuf: array of byte; 37osecbuf: array of byte; 38zeroes: array of byte; 39rdonly := 0; 40dowrite := 0; 41 42Prepedit: type Edit[string]; 43 44edit: ref Edit; 45 46Auto: adt 47{ 48 name: string; 49 min: big; 50 max: big; 51 weight: int; 52 alloc: int; 53 size: big; 54}; 55 56KB: con big 1024; 57MB: con KB*KB; 58GB: con KB*MB; 59 60# 61# Order matters -- this is the layout order on disk. 62# 63auto: array of Auto = array[] of { 64 ("9fat", big 10*MB, big 100*MB, 10, 0, big 0), 65 ("nvram", big 512, big 512, 1, 0, big 0), 66 ("fscfg", big 512, big 512, 1, 0, big 0), 67 ("fs", big 200*MB, big 0, 10, 0, big 0), 68 ("fossil", big 200*MB, big 0, 4, 0, big 0), 69 ("arenas", big 500*MB, big 0, 20, 0, big 0), 70 ("isect", big 25*MB, big 0, 1, 0, big 0), 71 ("other", big 200*MB, big 0, 4, 0, big 0), 72 ("swap", big 100*MB, big 512*MB, 1, 0, big 0), 73 ("cache", big 50*MB, big 1*GB, 2, 0, big 0), 74}; 75 76stderr: ref Sys->FD; 77 78init(nil: ref Draw->Context, args: list of string) 79{ 80 sys = load Sys Sys->PATH; 81 bufio = load Bufio Bufio->PATH; 82 disks = load Disks Disks->PATH; 83 pedit = load Pedit Pedit->PATH; 84 85 sys->pctl(Sys->FORKFD, nil); 86 disks->init(); 87 pedit->init(); 88 89 edit = Edit.mk("sector"); 90 91 edit.add = cmdadd; 92 edit.del = cmddel; 93 edit.okname = cmdokname; 94 edit.sum = cmdsum; 95 edit.write = cmdwrite; 96 97 stderr = sys->fildes(2); 98 secsize := 0; 99 arg := load Arg Arg->PATH; 100 arg->init(args); 101 arg->setusage("disk/prep [-bfprw] [-a partname]... [-s sectorsize] /dev/sdC0/plan9"); 102 while((o := arg->opt()) != 0) 103 case o { 104 'a' => 105 p := arg->earg(); 106 for(i:=0; i<len auto; i++){ 107 if(p == auto[i].name){ 108 if(auto[i].alloc){ 109 sys->fprint(stderr, "you said -a %s more than once.\n", p); 110 arg->usage(); 111 } 112 auto[i].alloc = 1; 113 break; 114 } 115 } 116 if(i == len auto){ 117 sys->fprint(stderr, "don't know how to create automatic partition %s\n", p); 118 arg->usage(); 119 } 120 doauto = 1; 121 'b' => 122 blank++; 123 'f' => 124 file++; 125 'p' => 126 printflag++; 127 rdonly++; 128 'r' => 129 rdonly++; 130 's' => 131 secsize = int arg->earg(); 132 'w' => 133 dowrite++; 134 * => 135 arg->usage(); 136 } 137 args = arg->argv(); 138 if(len args != 1) 139 arg->usage(); 140 arg = nil; 141 142 mode := Sys->ORDWR; 143 if(rdonly) 144 mode = Sys->OREAD; 145 disk := Disk.open(hd args, mode, file); 146 if(disk == nil) { 147 sys->fprint(stderr, "cannot open disk: %r\n"); 148 exits("opendisk"); 149 } 150 151 if(secsize != 0) { 152 disk.secsize = secsize; 153 disk.secs = disk.size / big secsize; 154 } 155 edit.end = disk.secs; 156 157 checkfat(disk); 158 159 secbuf = array[disk.secsize+1] of byte; 160 osecbuf = array[disk.secsize+1] of byte; 161 zeroes = array[disk.secsize+1] of {* => byte 0}; 162 edit.disk = disk; 163 164 if(blank == 0) 165 rdpart(edit); 166 167 # save old partition table 168 opart = array[len edit.part] of ref Part; 169 opart[0:] = edit.part; 170 171 if(printflag) { 172 edit.runcmd("P"); 173 exits(nil); 174 } 175 176 if(doauto) 177 autopart(edit); 178 179 if(dowrite) { 180 edit.runcmd("w"); 181 exits(nil); 182 } 183 184 edit.runcmd("p"); 185 for(;;) { 186 sys->fprint(stderr, ">>> "); 187 edit.runcmd(edit.getline()); 188 } 189} 190 191cmdsum(edit: ref Edit, p: ref Part, a: big, b: big) 192{ 193 c := ' '; 194 name := "empty"; 195 if(p != nil){ 196 if(p.changed) 197 c = '\''; 198 name = p.name; 199 } 200 201 sz := (b-a)*big edit.disk.secsize; 202 suf := "B "; 203 div := big 1; 204 if(sz >= big 1*GB){ 205 suf = "GB"; 206 div = GB; 207 }else if(sz >= big 1*MB){ 208 suf = "MB"; 209 div = MB; 210 }else if(sz >= big 1*KB){ 211 suf = "KB"; 212 div = KB; 213 } 214 215 if(div == big 1) 216 sys->print("%c %-12s %*bd %-*bd (%bd sectors, %bd %s)\n", c, name, 217 edit.disk.width, a, edit.disk.width, b, b-a, sz, suf); 218 else 219 sys->print("%c %-12s %*bd %-*bd (%bd sectors, %bd.%.2d %s)\n", c, name, 220 edit.disk.width, a, edit.disk.width, b, b-a, 221 sz/div, int (((sz%div)*big 100)/div), suf); 222} 223 224cmdadd(edit: ref Edit, name: string, start: big, end: big): string 225{ 226 if(start < big 2 && name == "9fat") 227 return "overlaps with the pbs and/or the partition table"; 228 229 return edit.addpart(mkpart(name, start, end, 1)); 230} 231 232cmddel(edit: ref Edit, p: ref Part): string 233{ 234 return edit.delpart(p); 235} 236 237cmdwrite(edit: ref Edit): string 238{ 239 wrpart(edit); 240 return nil; 241} 242 243isfrog := array[256] of { 244 byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # NUL 245 byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # BKS 246 byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # DLE 247 byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, byte 1, # CAN 248 ' ' => byte 1, 249 '/' => byte 1, 250 16r7f=> byte 1, 251 * => byte 0 252}; 253 254cmdokname(nil: ref Edit, elem: string): string 255{ 256 for(i := 0; i < len elem; i++) 257 if(int isfrog[elem[i]]) 258 return "bad character in name"; 259 return nil; 260} 261 262mkpart(name: string, start: big, end: big, changed: int): ref Part 263{ 264 p := ref Part; 265 p.name = name; 266 p.ctlname = name; 267 p.start = start; 268 p.end = end; 269 p.changed = changed; 270 p.ctlstart = big 0; 271 p.ctlend = big 0; 272 return p; 273} 274 275# plan9 partition table is first sector of the disk 276 277rdpart(edit: ref Edit) 278{ 279 disk := edit.disk; 280 sys->seek(disk.fd, big disk.secsize, 0); 281 if(sys->readn(disk.fd, osecbuf, disk.secsize) != disk.secsize) 282 return; 283 osecbuf[disk.secsize] = byte 0; 284 secbuf[0:] = osecbuf; 285 286 for(i := 0; i < disk.secsize; i++) 287 if(secbuf[i] == byte 0) 288 break; 289 290 tab := string secbuf[0:i]; 291 if(len tab < 4 || tab[0:4] != "part"){ 292 sys->fprint(stderr, "no plan9 partition table found\n"); 293 return; 294 } 295 296 waserr := 0; 297 (nline, lines) := sys->tokenize(tab, "\n"); 298 for(i=0; i<nline; i++){ 299 line := hd lines; 300 lines = tl lines; 301 if(len line < 4 || line[0:4] != "part"){ 302 waserr = 1; 303 continue; 304 } 305 306 (nf, f) := sys->tokenize(line, " \t\r"); 307 if(nf != 4 || hd f != "part"){ 308 waserr = 1; 309 continue; 310 } 311 312 a := big hd tl tl f; 313 b := big hd tl tl tl f; 314 if(a >= b){ 315 waserr = 1; 316 continue; 317 } 318 319 if((err := edit.addpart(mkpart(hd tl f, a, b, 0))) != nil) { 320 sys->fprint(stderr, "?%s: not continuing\n", err); 321 exits("partition"); 322 } 323 } 324 if(waserr) 325 sys->fprint(stderr, "syntax error reading partition\n"); 326} 327 328min(a, b: big): big 329{ 330 if(a < b) 331 return a; 332 return b; 333} 334 335autopart(edit: ref Edit) 336{ 337 if(len edit.part > 0) { 338 if(doauto) 339 sys->fprint(stderr, "partitions already exist; not repartitioning\n"); 340 return; 341 } 342 343 secs := edit.disk.secs; 344 secsize := big edit.disk.secsize; 345 for(;;){ 346 # compute total weights 347 totw := 0; 348 for(i:=0; i<len auto; i++){ 349 if(auto[i].alloc==0 || auto[i].size != big 0) 350 continue; 351 totw += auto[i].weight; 352 } 353 if(totw == 0) 354 break; 355 356 if(secs <= big 0){ 357 sys->fprint(stderr, "ran out of disk space during autopartition.\n"); 358 return; 359 } 360 361 # assign any minimums for small disks 362 futz := 0; 363 for(i=0; i<len auto; i++){ 364 if(auto[i].alloc==0 || auto[i].size != big 0) 365 continue; 366 s := (secs*big auto[i].weight)/big totw; 367 if(s < big auto[i].min/secsize){ 368 auto[i].size = big auto[i].min/secsize; 369 secs -= auto[i].size; 370 futz = 1; 371 break; 372 } 373 } 374 if(futz) 375 continue; 376 377 # assign any maximums for big disks 378 futz = 0; 379 for(i=0; i<len auto; i++){ 380 if(auto[i].alloc==0 || auto[i].size != big 0) 381 continue; 382 s := (secs*big auto[i].weight)/big totw; 383 if(auto[i].max != big 0 && s > auto[i].max/secsize){ 384 auto[i].size = auto[i].max/secsize; 385 secs -= auto[i].size; 386 futz = 1; 387 break; 388 } 389 } 390 if(futz) 391 continue; 392 393 # finally, assign partition sizes according to weights 394 for(i=0; i<len auto; i++){ 395 if(auto[i].alloc==0 || auto[i].size != big 0) 396 continue; 397 s := (secs*big auto[i].weight)/big totw; 398 auto[i].size = s; 399 400 # use entire disk even in face of rounding errors 401 secs -= auto[i].size; 402 totw -= auto[i].weight; 403 } 404 } 405 406 for(i:=0; i<len auto; i++) 407 if(auto[i].alloc) 408 sys->print("%s %bud\n", auto[i].name, auto[i].size); 409 410 s := big 0; 411 for(i=0; i<len auto; i++){ 412 if(auto[i].alloc == 0) 413 continue; 414 if((err := edit.addpart(mkpart(auto[i].name, s, s+auto[i].size, 1))) != nil) 415 sys->fprint(stderr, "addpart %s: %s\n", auto[i].name, err); 416 s += auto[i].size; 417 } 418} 419 420restore(edit: ref Edit, ctlfd: ref Sys->FD) 421{ 422 offset := edit.disk.offset; 423 sys->fprint(stderr, "attempting to restore partitions to previous state\n"); 424 if(sys->seek(edit.disk.wfd, big edit.disk.secsize, 0) != big 0){ 425 sys->fprint(stderr, "cannot restore: error seeking on disk: %r\n"); 426 exits("inconsistent"); 427 } 428 429 if(sys->write(edit.disk.wfd, osecbuf, edit.disk.secsize) != edit.disk.secsize){ 430 sys->fprint(stderr, "cannot restore: couldn't write old partition table to disk: %r\n"); 431 exits("inconsistent"); 432 } 433 434 if(ctlfd != nil){ 435 for(i:=0; i<len edit.part; i++) 436 sys->fprint(ctlfd, "delpart %s", edit.part[i].name); 437 for(i=0; i<len opart; i++){ 438 if(sys->fprint(ctlfd, "part %s %bd %bd", opart[i].name, opart[i].start+offset, opart[i].end+offset) < 0){ 439 sys->fprint(stderr, "restored disk partition table but not kernel table; reboot\n"); 440 exits("inconsistent"); 441 } 442 } 443 } 444 exits("restored"); 445} 446 447wrpart(edit: ref Edit) 448{ 449 disk := edit.disk; 450 451 secbuf[0:] = zeroes; 452 n := 0; 453 for(i:=0; i<len edit.part; i++){ 454 a := sys->aprint("part %s %bd %bd\n", 455 edit.part[i].name, edit.part[i].start, edit.part[i].end); 456 if(n + len a > disk.secsize){ 457 sys->fprint(stderr, "partition table bigger than sector (%d bytes)\n", disk.secsize); 458 exits("overflow"); 459 } 460 secbuf[n:] = a; 461 n += len a; 462 } 463 464 if(sys->seek(disk.wfd, big disk.secsize, 0) != big disk.secsize){ 465 sys->fprint(stderr, "error seeking to %d on disk: %r\n", disk.secsize); 466 exits("seek"); 467 } 468 469 if(sys->write(disk.wfd, secbuf, disk.secsize) != disk.secsize){ 470 sys->fprint(stderr, "error writing partition table to disk: %r\n"); 471 restore(edit, nil); 472 } 473 474 if(edit.ctldiff(disk.ctlfd) < 0) 475 sys->fprint(stderr, "?warning: partitions could not be updated in devsd\n"); 476} 477 478# 479# Look for a boot sector in sector 1, as would be 480# the case if editing /dev/sdC0/data when that 481# was really a bootable disk. 482# 483checkfat(disk: ref Disk) 484{ 485 buf := array[32] of byte; 486 487 if(sys->seek(disk.fd, big disk.secsize, 0) != big disk.secsize || 488 sys->read(disk.fd, buf, len buf) < len buf) 489 return; 490 491 if(buf[0] != byte 16rEB || buf[1] != byte 16r3C || buf[2] != byte 16r90) 492 return; 493 494 sys->fprint(stderr, 495 "there's a fat partition where the\n"+ 496 "plan9 partition table would go.\n"+ 497 "if you really want to overwrite it, zero\n"+ 498 "the second sector of the disk and try again\n"); 499 500 exits("fat partition"); 501} 502 503exits(s: string) 504{ 505 if(s != nil) 506 raise "fail:"+s; 507 exit; 508} 509