1 /* 2 * disk partition editor 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <bio.h> 7 #include <ctype.h> 8 #include <disk.h> 9 #include "edit.h" 10 11 char* 12 getline(Edit *edit) 13 { 14 static int inited; 15 static Biobuf bin; 16 char *p; 17 int n; 18 19 if(!inited){ 20 Binit(&bin, 0, OREAD); 21 inited = 1; 22 } 23 p = Brdline(&bin, '\n'); 24 n = Blinelen(&bin); 25 if(p == nil || n < 1){ 26 if(edit->changed) 27 fprint(2, "?warning: changes not written\n"); 28 exits(0); 29 } 30 p[n - 1] = '\0'; 31 while(isspace(*p)) 32 p++; 33 return p; 34 } 35 36 Part* 37 findpart(Edit *edit, char *name) 38 { 39 int i; 40 41 for(i=0; i<edit->npart; i++) 42 if(strcmp(edit->part[i]->name, name) == 0) 43 return edit->part[i]; 44 return nil; 45 } 46 47 static char* 48 okname(Edit *edit, char *name) 49 { 50 int i; 51 static char msg[100]; 52 53 if(name[0] == '\0') 54 return "partition has no name"; 55 56 // if(strlen(name) >= NAMELEN) 57 // return "name too long"; 58 // 59 for(i=0; i<edit->npart; i++) { 60 if(strcmp(name, edit->part[i]->name) == 0) { 61 sprint(msg, "already have partition with name \"%s\"", name); 62 return msg; 63 } 64 } 65 return nil; 66 } 67 68 char* 69 addpart(Edit *edit, Part *p) 70 { 71 int i; 72 static char msg[100]; 73 char *err; 74 75 if(err = okname(edit, p->name)) 76 return err; 77 78 for(i=0; i<edit->npart; i++) { 79 if(p->start < edit->part[i]->end && edit->part[i]->start < p->end) { 80 sprint(msg, "\"%s\" %lld-%lld overlaps with \"%s\" %lld-%lld", 81 p->name, p->start, p->end, 82 edit->part[i]->name, edit->part[i]->start, edit->part[i]->end); 83 // return msg; 84 } 85 } 86 87 if(edit->npart >= nelem(edit->part)) 88 return "too many partitions"; 89 90 edit->part[i=edit->npart++] = p; 91 for(; i > 0 && p->start < edit->part[i-1]->start; i--) { 92 edit->part[i] = edit->part[i-1]; 93 edit->part[i-1] = p; 94 } 95 96 if(p->changed) 97 edit->changed = 1; 98 return nil; 99 } 100 101 char* 102 delpart(Edit *edit, Part *p) 103 { 104 int i; 105 106 for(i=0; i<edit->npart; i++) 107 if(edit->part[i] == p) 108 break; 109 assert(i < edit->npart); 110 edit->npart--; 111 for(; i<edit->npart; i++) 112 edit->part[i] = edit->part[i+1]; 113 114 edit->changed = 1; 115 return nil; 116 } 117 118 static char* 119 editdot(Edit *edit, int argc, char **argv) 120 { 121 char *err; 122 vlong ndot; 123 124 if(argc == 1) { 125 print("\t. %lld\n", edit->dot); 126 return nil; 127 } 128 129 if(argc > 2) 130 return "args"; 131 132 if(err = parseexpr(argv[1], edit->dot, edit->end, edit->end, &ndot)) 133 return err; 134 135 edit->dot = ndot; 136 return nil; 137 } 138 139 static char* 140 editadd(Edit *edit, int argc, char **argv) 141 { 142 char *name, *err, *q; 143 static char msg[100]; 144 vlong start, end, maxend; 145 int i; 146 147 if(argc < 2) 148 return "args"; 149 150 name = estrdup(argv[1]); 151 if((err = okname(edit, name)) || (err = edit->okname(edit, name))) 152 return err; 153 154 if(argc >= 3) 155 q = argv[2]; 156 else { 157 fprint(2, "start %s: ", edit->unit); 158 q = getline(edit); 159 } 160 if(err = parseexpr(q, edit->dot, edit->end, edit->end, &start)) 161 return err; 162 163 if(start < 0 || start >= edit->end) 164 return "start out of range"; 165 166 for(i=0; i < edit->npart; i++) { 167 if(edit->part[i]->start <= start && start < edit->part[i]->end) { 168 sprint(msg, "start %s in partition \"%s\"", edit->unit, edit->part[i]->name); 169 return msg; 170 } 171 } 172 173 maxend = edit->end; 174 for(i=0; i < edit->npart; i++) 175 if(start < edit->part[i]->start && edit->part[i]->start < maxend) 176 maxend = edit->part[i]->start; 177 178 if(argc >= 4) 179 q = argv[3]; 180 else { 181 fprint(2, "end [%lld..%lld] ", start, maxend); 182 q = getline(edit); 183 } 184 if(err = parseexpr(q, edit->dot, maxend, edit->end, &end)) 185 return err; 186 187 if(start == end) 188 return "size zero partition"; 189 190 if(end <= start || end > maxend) 191 return "end out of range"; 192 193 if(argc > 4) 194 return "args"; 195 196 if(err = edit->add(edit, name, start, end)) 197 return err; 198 199 edit->dot = end; 200 return nil; 201 } 202 203 static char* 204 editdel(Edit *edit, int argc, char **argv) 205 { 206 Part *p; 207 208 if(argc != 2) 209 return "args"; 210 211 if((p = findpart(edit, argv[1])) == nil) 212 return "no such partition"; 213 214 return edit->del(edit, p); 215 } 216 217 static char *helptext = 218 ". [newdot] - display or set value of dot\n" 219 "a name [start [end]] - add partition\n" 220 "d name - delete partition\n" 221 "h - print help message\n" 222 "p - print partition table\n" 223 "P - print commands to update sd(3) device\n" 224 "w - write partition table\n" 225 "q - quit\n"; 226 227 static char* 228 edithelp(Edit *edit, int, char**) 229 { 230 print("%s", helptext); 231 if(edit->help) 232 return edit->help(edit); 233 return nil; 234 } 235 236 static char* 237 editprint(Edit *edit, int argc, char**) 238 { 239 vlong lastend; 240 int i; 241 Part **part; 242 243 if(argc != 1) 244 return "args"; 245 246 lastend = 0; 247 part = edit->part; 248 for(i=0; i<edit->npart; i++) { 249 if(lastend < part[i]->start) 250 edit->sum(edit, nil, lastend, part[i]->start); 251 edit->sum(edit, part[i], part[i]->start, part[i]->end); 252 lastend = part[i]->end; 253 } 254 if(lastend < edit->end) 255 edit->sum(edit, nil, lastend, edit->end); 256 return nil; 257 } 258 259 char* 260 editwrite(Edit *edit, int argc, char**) 261 { 262 int i; 263 char *err; 264 265 if(argc != 1) 266 return "args"; 267 268 if(edit->disk->rdonly) 269 return "read only"; 270 271 err = edit->write(edit); 272 if(err) 273 return err; 274 for(i=0; i<edit->npart; i++) 275 edit->part[i]->changed = 0; 276 edit->changed = 0; 277 return nil; 278 } 279 280 static char* 281 editquit(Edit *edit, int argc, char**) 282 { 283 static int warned; 284 285 if(argc != 1) { 286 warned = 0; 287 return "args"; 288 } 289 290 if(edit->changed && (!edit->warned || edit->lastcmd != 'q')) { 291 edit->warned = 1; 292 return "changes unwritten"; 293 } 294 295 exits(0); 296 return nil; /* not reached */ 297 } 298 299 char* 300 editctlprint(Edit *edit, int argc, char **) 301 { 302 if(argc != 1) 303 return "args"; 304 305 if(edit->printctl) 306 edit->printctl(edit, 1); 307 else 308 ctldiff(edit, 1); 309 return nil; 310 } 311 312 typedef struct Cmd Cmd; 313 struct Cmd { 314 char c; 315 char *(*fn)(Edit*, int ,char**); 316 }; 317 318 Cmd cmds[] = { 319 '.', editdot, 320 'a', editadd, 321 'd', editdel, 322 '?', edithelp, 323 'h', edithelp, 324 'P', editctlprint, 325 'p', editprint, 326 'w', editwrite, 327 'q', editquit, 328 }; 329 330 void 331 runcmd(Edit *edit, char *cmd) 332 { 333 char *f[10], *err; 334 int i, nf; 335 336 while(*cmd && isspace(*cmd)) 337 cmd++; 338 339 nf = tokenize(cmd, f, nelem(f)); 340 if(nf >= 10) { 341 fprint(2, "?\n"); 342 return; 343 } 344 345 if(nf < 1) 346 return; 347 if(strlen(f[0]) != 1) { 348 fprint(2, "?\n"); 349 return; 350 } 351 352 err = nil; 353 for(i=0; i<nelem(cmds); i++) { 354 if(cmds[i].c == f[0][0]) { 355 err = cmds[i].fn(edit, nf, f); 356 break; 357 } 358 } 359 if(i == nelem(cmds)){ 360 if(edit->ext) 361 err = edit->ext(edit, nf, f); 362 else 363 err = "unknown command"; 364 } 365 if(err) 366 fprint(2, "?%s\n", err); 367 edit->lastcmd = f[0][0]; 368 } 369 370 static Part* 371 ctlmkpart(char *name, vlong start, vlong end, int changed) 372 { 373 Part *p; 374 375 p = emalloc(sizeof(*p)); 376 p->name = estrdup(name); 377 p->ctlname = estrdup(name); 378 p->start = start; 379 p->end = end; 380 p->changed = changed; 381 return p; 382 } 383 384 static void 385 rdctlpart(Edit *edit) 386 { 387 int i, nline, nf; 388 char *line[128]; 389 char buf[4096]; 390 vlong a, b; 391 char *f[5]; 392 Disk *disk; 393 394 disk = edit->disk; 395 edit->nctlpart = 0; 396 seek(disk->ctlfd, 0, 0); 397 if(readn(disk->ctlfd, buf, sizeof buf) <= 0) { 398 return; 399 } 400 401 nline = getfields(buf, line, nelem(line), 1, "\n"); 402 for(i=0; i<nline; i++){ 403 if(strncmp(line[i], "part ", 5) != 0) 404 continue; 405 406 nf = getfields(line[i], f, nelem(f), 1, " \t\r"); 407 if(nf != 4 || strcmp(f[0], "part") != 0) 408 break; 409 410 a = strtoll(f[2], 0, 0); 411 b = strtoll(f[3], 0, 0); 412 413 if(a >= b) 414 break; 415 416 /* only gather partitions contained in the disk partition we are editing */ 417 if(a < disk->offset || disk->offset+disk->secs < b) 418 continue; 419 420 a -= disk->offset; 421 b -= disk->offset; 422 423 /* the partition we are editing does not count */ 424 if(strcmp(f[1], disk->part) == 0) 425 continue; 426 427 if(edit->nctlpart >= nelem(edit->ctlpart)) { 428 fprint(2, "?too many partitions in ctl file\n"); 429 exits("ctlpart"); 430 } 431 edit->ctlpart[edit->nctlpart++] = ctlmkpart(f[1], a, b, 0); 432 } 433 } 434 435 static vlong 436 ctlstart(Part *p) 437 { 438 if(p->ctlstart) 439 return p->ctlstart; 440 return p->start; 441 } 442 443 static vlong 444 ctlend(Part *p) 445 { 446 if(p->ctlend) 447 return p->ctlend; 448 return p->end; 449 } 450 451 static int 452 areequiv(Part *p, Part *q) 453 { 454 if(p->ctlname[0]=='\0' || q->ctlname[0]=='\0') 455 return 0; 456 457 return strcmp(p->ctlname, q->ctlname) == 0 458 && ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q); 459 } 460 461 static void 462 unchange(Edit *edit, Part *p) 463 { 464 int i; 465 Part *q; 466 467 for(i=0; i<edit->nctlpart; i++) { 468 q = edit->ctlpart[i]; 469 if(p->start <= q->start && q->end <= p->end) { 470 q->changed = 0; 471 } 472 } 473 assert(p->changed == 0); 474 } 475 476 int 477 ctldiff(Edit *edit, int ctlfd) 478 { 479 int i, j, waserr; 480 Part *p; 481 vlong offset; 482 483 rdctlpart(edit); 484 485 /* everything is bogus until we prove otherwise */ 486 for(i=0; i<edit->nctlpart; i++) 487 edit->ctlpart[i]->changed = 1; 488 489 /* 490 * partitions with same info have not changed, 491 * and neither have partitions inside them. 492 */ 493 for(i=0; i<edit->nctlpart; i++) 494 for(j=0; j<edit->npart; j++) 495 if(areequiv(edit->ctlpart[i], edit->part[j])) { 496 unchange(edit, edit->ctlpart[i]); 497 break; 498 } 499 500 waserr = 0; 501 /* 502 * delete all the changed partitions except data (we'll add them back if necessary) 503 */ 504 for(i=0; i<edit->nctlpart; i++) { 505 p = edit->ctlpart[i]; 506 if(p->changed) 507 if(fprint(ctlfd, "delpart %s\n", p->ctlname)<0) { 508 fprint(2, "delpart failed: %s: %r\n", p->ctlname); 509 waserr = -1; 510 } 511 } 512 513 /* 514 * add all the partitions from the real list; 515 * this is okay since adding a parition with 516 * information identical to what is there is a no-op. 517 */ 518 offset = edit->disk->offset; 519 for(i=0; i<edit->npart; i++) { 520 p = edit->part[i]; 521 if(p->ctlname[0]) { 522 if(fprint(ctlfd, "part %s %lld %lld\n", p->ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) { 523 fprint(2, "adding part failed: %s: %r\n", p->ctlname); 524 waserr = -1; 525 } 526 } 527 } 528 return waserr; 529 } 530 531 void* 532 emalloc(ulong sz) 533 { 534 void *v; 535 536 v = malloc(sz); 537 if(v == nil) 538 sysfatal("malloc %lud fails", sz); 539 memset(v, 0, sz); 540 return v; 541 } 542 543 char* 544 estrdup(char *s) 545 { 546 s = strdup(s); 547 if(s == nil) 548 sysfatal("strdup (%.10s) fails", s); 549 return s; 550 } 551 552