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