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