1 /* part 1.57 - Partition table editor Author: Kees J. Bot 2 * 13 Mar 1992 3 * Needs about 22k heap+stack. 4 */ 5 #define nil 0 6 #include <sys/types.h> 7 #include <stdio.h> 8 #include <termcap.h> 9 #include <errno.h> 10 #include <unistd.h> 11 #include <stddef.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <signal.h> 15 #include <fcntl.h> 16 #include <time.h> 17 #include <dirent.h> 18 #include <limits.h> 19 #include <sys/stat.h> 20 #include <sys/wait.h> 21 #include <sys/ioctl.h> 22 #include <minix/config.h> 23 #include <minix/const.h> 24 #include <minix/partition.h> 25 #include <machine/partition.h> 26 #include <termios.h> 27 28 /* True if a partition is an extended partition. */ 29 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F) 30 31 /* Minix master bootstrap code. */ 32 static char MASTERBOOT[] = "/usr/mdec/mbr"; 33 34 /* Template: 35 ----first---- --geom/last-- ------sectors----- 36 Device Cyl Head Sec Cyl Head Sec Base Size Kb 37 /dev/c0d0 977 5 17 38 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521 39 Num Sort Type 40 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440 41 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142 42 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939 43 3 p3 00 None 0 0 0 0 0 -1 0 0 0 44 45 */ 46 #define MAXSIZE 999999999L /* Will 1T be enough this year? */ 47 #define SECTOR_SIZE 512 48 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */ 49 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */ 50 51 #define arraysize(a) (sizeof(a) / sizeof((a)[0])) 52 #define arraylimit(a) ((a) + arraysize(a)) 53 54 void report(const char *label) 55 { 56 fprintf(stderr, "part: %s: %s\n", label, strerror(errno)); 57 } 58 59 void fatal(const char *label) 60 { 61 report(label); 62 exit(1); 63 } 64 65 struct termios termios; 66 67 void save_ttyflags(void) 68 /* Save tty attributes for later restoration. */ 69 { 70 if (tcgetattr(0, &termios) < 0) fatal(""); 71 } 72 73 void restore_ttyflags(void) 74 /* Reset the tty flags to how we got 'em. */ 75 { 76 if (tcsetattr(0, TCSANOW, &termios) < 0) fatal(""); 77 } 78 79 void tty_raw(void) 80 /* Set the terminal to raw mode, no signals, no echoing. */ 81 { 82 struct termios rawterm; 83 84 rawterm= termios; 85 rawterm.c_lflag &= ~(ICANON|ISIG|ECHO); 86 rawterm.c_iflag &= ~(ICRNL); 87 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal(""); 88 } 89 90 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37')) 91 92 char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16]; 93 int t_li, t_co; 94 #define STATUSROW 10 95 96 void init_tty(void) 97 /* Get terminal capabilities and set the tty to "editor" mode. */ 98 { 99 char *term; 100 static char termbuf[1024]; 101 char *tp; 102 103 if ((term= getenv("TERM")) == nil || tgetent(termbuf, term) != 1) { 104 fprintf(stderr, "part: Can't get terminal capabilities\n"); 105 exit(1); 106 } 107 if (tgetstr("cd", (tp= t_cd, &tp)) == nil 108 || tgetstr("cm", (tp= t_cm, &tp)) == nil) { 109 fprintf(stderr, "part: This terminal is too dumb\n"); 110 exit(1); 111 } 112 t_li= tgetnum("li"); 113 t_co= tgetnum("co"); 114 (void) tgetstr("so", (tp= t_so, &tp)); 115 (void) tgetstr("se", (tp= t_se, &tp)); 116 (void) tgetstr("md", (tp= t_md, &tp)); 117 (void) tgetstr("me", (tp= t_me, &tp)); 118 119 save_ttyflags(); 120 tty_raw(); 121 } 122 123 int putchr(int c) 124 { 125 return putchar(c); 126 } 127 128 void putstr(char *s) 129 { 130 int c; 131 132 while ((c= *s++) != 0) putchr(c); 133 } 134 135 void set_cursor(int row, int col) 136 { 137 tputs(tgoto(t_cm, col, row), 1, putchr); 138 } 139 140 int statusrow= STATUSROW; 141 int stat_ktl= 1; 142 int need_help= 1; 143 144 void stat_start(int serious) 145 /* Prepare for printing on a fresh status line, possibly highlighted. */ 146 { 147 set_cursor(statusrow++, 0); 148 tputs(t_cd, 1, putchr); 149 if (serious) tputs(t_so, 1, putchr); 150 } 151 152 void stat_end(int ktl) 153 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */ 154 { 155 tputs(t_se, 1, putchr); 156 stat_ktl= ktl; 157 need_help= 1; 158 } 159 160 void stat_reset(void) 161 /* Reset the statusline pointer and clear old messages if expired. */ 162 { 163 if (stat_ktl > 0 && --stat_ktl == 0) { 164 statusrow= STATUSROW; 165 need_help= 1; 166 } 167 if (need_help && statusrow < (24-2)) { 168 if (statusrow > STATUSROW) stat_start(0); 169 stat_start(0); 170 putstr( 171 "Type '+' or '-' to change, 'r' to read, '?' for more help, 'q' to exit"); 172 } 173 statusrow= STATUSROW; 174 need_help= 0; 175 } 176 177 void clear_screen(void) 178 { 179 set_cursor(0, 0); 180 tputs(t_cd, 1, putchr); 181 stat_ktl= 1; 182 stat_reset(); 183 } 184 185 void reset_tty(void) 186 /* Reset the tty to cooked mode. */ 187 { 188 restore_ttyflags(); 189 set_cursor(statusrow, 0); 190 tputs(t_cd, 1, putchr); 191 } 192 193 void *alloc(size_t n) 194 { 195 void *m; 196 197 if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); } 198 199 return m; 200 } 201 202 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t; 203 204 typedef struct device { 205 struct device *next, *prev; /* Circular dequeue. */ 206 dev_t rdev; /* Device number (sorting only). */ 207 char *name; /* E.g. /dev/c0d0 */ 208 char *subname; /* E.g. /dev/c0d0:2 */ 209 parttype_t parttype; 210 } device_t; 211 212 device_t *firstdev= nil, *curdev; 213 214 void newdevice(char *name, int scanning) 215 /* Add a device to the device list. If scanning is set then we are reading 216 * /dev, so insert the device in device number order and make /dev/c0d0 current. 217 */ 218 { 219 device_t *new, *nextdev, *prevdev; 220 struct stat st; 221 222 st.st_rdev= 0; 223 if (scanning) { 224 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return; 225 226 switch (major(st.st_rdev)) { 227 case 2: 228 /* Floppy */ 229 if (minor(st.st_rdev) >= 4) return; 230 break; 231 case 3: 232 case 8: 233 case 10: 234 case 12: 235 case 16: 236 /* Disk controller */ 237 if (minor(st.st_rdev) >= 0x80 238 || minor(st.st_rdev) % 5 != 0) return; 239 break; 240 default: 241 return; 242 } 243 /* Interesting device found. */ 244 } else { 245 (void) stat(name, &st); 246 } 247 248 new= alloc(sizeof(*new)); 249 new->rdev= st.st_rdev; 250 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0])); 251 strcpy(new->name, name); 252 new->subname= new->name; 253 new->parttype= DUNNO; 254 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) { 255 new->parttype= FLOPPY; 256 } else 257 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128 258 && minor(st.st_rdev) % 5 == 0) { 259 new->parttype= PRIMARY; 260 } 261 262 if (firstdev == nil) { 263 firstdev= new; 264 new->next= new->prev= new; 265 curdev= firstdev; 266 return; 267 } 268 nextdev= firstdev; 269 while (new->rdev >= nextdev->rdev 270 && (nextdev= nextdev->next) != firstdev) {} 271 prevdev= nextdev->prev; 272 new->next= nextdev; 273 nextdev->prev= new; 274 new->prev= prevdev; 275 prevdev->next= new; 276 277 if (new->rdev < firstdev->rdev) firstdev= new; 278 if (new->rdev == DEV_C0D0) curdev= new; 279 if (curdev->rdev != DEV_C0D0) curdev= firstdev; 280 } 281 282 void getdevices(void) 283 /* Get all block devices from /dev that look interesting. */ 284 { 285 DIR *d; 286 struct dirent *e; 287 char name[5 + NAME_MAX + 1]; 288 289 if ((d= opendir("/dev")) == nil) fatal("/dev"); 290 291 while ((e= readdir(d)) != nil) { 292 strcpy(name, "/dev/"); 293 strcpy(name + 5, e->d_name); 294 newdevice(name, 1); 295 } 296 (void) closedir(d); 297 } 298 299 int dirty= 0; 300 unsigned char bootblock[SECTOR_SIZE]; 301 struct part_entry table[1 + NR_PARTITIONS]; 302 int existing[1 + NR_PARTITIONS]; 303 unsigned long offset= 0, extbase= 0, extsize; 304 int submerged= 0; 305 char sort_index[1 + NR_PARTITIONS]; 306 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1; 307 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1; 308 int precise= 0; 309 int device= -1; 310 311 unsigned long sortbase(struct part_entry *pe) 312 { 313 return pe->sysind == NO_PART ? -1 : pe->lowsec; 314 } 315 316 void sort(void) 317 /* Let the sort_index array show the order partitions are sorted in. */ 318 { 319 int i, j; 320 int idx[1 + NR_PARTITIONS]; 321 322 for (i= 1; i <= NR_PARTITIONS; i++) idx[i]= i; 323 324 for (i= 1; i <= NR_PARTITIONS; i++) { 325 for (j= 1; j <= NR_PARTITIONS-1; j++) { 326 int sj= idx[j], sj1= idx[j+1]; 327 328 if (sortbase(&table[sj]) > sortbase(&table[sj1])) { 329 idx[j]= sj1; 330 idx[j+1]= sj; 331 } 332 } 333 } 334 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[idx[i]]= i; 335 } 336 337 void dos2chs(unsigned char *dos, unsigned *chs) 338 /* Extract cylinder, head and sector from the three bytes DOS uses to address 339 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7 340 * of the sector byte. The sector number is rebased to count from 0. 341 */ 342 { 343 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2]; 344 chs[1]= dos[0]; 345 chs[2]= (dos[1] & 0x3F) - 1; 346 } 347 348 void abs2dos(unsigned char *dos, unsigned long pos) 349 /* Translate a sector offset to three DOS bytes. */ 350 { 351 unsigned h, c, s; 352 353 c= pos / secpcyl; 354 h= (pos % secpcyl) / sectors; 355 s= pos % sectors + 1; 356 357 dos[0]= h; 358 dos[1]= s | ((c >> 2) & 0xC0); 359 dos[2]= c & 0xFF; 360 } 361 362 void recompute0(void) 363 /* Recompute the partition size for the device after a geometry change. */ 364 { 365 if (device < 0) { 366 cylinders= heads= sectors= 1; 367 memset(table, 0, sizeof(table)); 368 } else 369 if (!precise && offset == 0) { 370 table[0].lowsec= 0; 371 table[0].size= (unsigned long) cylinders * heads * sectors; 372 } 373 table[0].sysind= device < 0 ? NO_PART : MINIX_PART; 374 secpcyl= heads * sectors; 375 } 376 377 void guess_geometry(void) 378 /* With a bit of work one can deduce the disk geometry from the partition 379 * table. This may be necessary if the driver gets it wrong. (If partition 380 * tables didn't have C/H/S numbers we would not care at all...) 381 */ 382 { 383 int i, n; 384 struct part_entry *pe; 385 unsigned chs[3]; 386 unsigned long sec; 387 unsigned h, s; 388 unsigned char HS[256][8]; /* Bit map off all possible H/S */ 389 390 alt_cyls= alt_heads= alt_secs= 0; 391 392 /* Initially all possible H/S combinations are possible. HS[h][0] 393 * bit 0 is used to rule out a head value. 394 */ 395 for (h= 1; h <= 255; h++) { 396 for (s= 0; s < 8; s++) HS[h][s]= 0xFF; 397 } 398 399 for (i= 0; i < 2*NR_PARTITIONS; i++) { 400 pe= &(table+1)[i >> 1]; 401 if (pe->sysind == NO_PART) continue; 402 403 /* Get the end or start sector numbers (in that order). */ 404 if ((i & 1) == 0) { 405 dos2chs(&pe->last_head, chs); 406 sec= pe->lowsec + pe->size - 1; 407 } else { 408 dos2chs(&pe->start_head, chs); 409 sec= pe->lowsec; 410 } 411 412 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1; 413 414 /* Which H/S combinations can be ruled out? */ 415 for (h= 1; h <= 255; h++) { 416 if (HS[h][0] == 0) continue; 417 n = 0; 418 for (s= 1; s <= 63; s++) { 419 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) { 420 HS[h][s/8] &= ~(1 << (s%8)); 421 } 422 if (HS[h][s/8] & (1 << (s%8))) n++; 423 } 424 if (n == 0) HS[h][0]= 0; 425 } 426 } 427 428 /* See if only one remains. */ 429 i= 0; 430 for (h= 1; h <= 255; h++) { 431 if (HS[h][0] == 0) continue; 432 for (s= 1; s <= 63; s++) { 433 if (HS[h][s/8] & (1 << (s%8))) { 434 i++; 435 alt_heads= h; 436 alt_secs= s; 437 } 438 } 439 } 440 441 /* Forget it if more than one choice... */ 442 if (i > 1) alt_cyls= alt_heads= alt_secs= 0; 443 } 444 445 void geometry(void) 446 /* Find out the geometry of the device by querying the driver, or by looking 447 * at the partition table. These numbers are crosschecked to make sure that 448 * the geometry is correct. Master bootstraps other than the Minix one use 449 * the CHS numbers in the partition table to load the bootstrap of the active 450 * partition. 451 */ 452 { 453 struct stat dst; 454 int err= 0; 455 struct part_geom geometry; 456 457 if (submerged) { 458 /* Geometry already known. */ 459 sort(); 460 return; 461 } 462 precise= 0; 463 cylinders= 0; 464 recompute0(); 465 if (device < 0) return; 466 467 /* Try to guess the geometry from the partition table. */ 468 guess_geometry(); 469 470 /* Try to get the geometry from the driver. */ 471 (void) fstat(device, &dst); 472 473 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) { 474 /* Try to get the drive's geometry from the driver. */ 475 476 if (ioctl(device, DIOCGETP, &geometry) < 0) 477 err= errno; 478 else { 479 table[0].lowsec= (unsigned long)(geometry.base / 480 SECTOR_SIZE); 481 table[0].size= (unsigned long)(geometry.size / 482 SECTOR_SIZE); 483 cylinders= geometry.cylinders; 484 heads= geometry.heads; 485 sectors= geometry.sectors; 486 precise= 1; 487 } 488 } else { 489 err= ENODEV; 490 } 491 492 if (err != 0) { 493 /* Getting the geometry from the driver failed, so use the 494 * alternate geometry. 495 */ 496 if (alt_heads == 0) { 497 alt_cyls= table[0].size / (64 * 32); 498 alt_heads= 64; 499 alt_secs= 32; 500 } 501 502 cylinders= alt_cyls; 503 heads= alt_heads; 504 sectors= alt_secs; 505 506 stat_start(1); 507 printf("Failure to get the geometry of %s: %s", curdev->name, 508 errno == ENOTTY ? "No driver support" : strerror(err)); 509 stat_end(5); 510 stat_start(0); 511 printf("The geometry has been guessed as %ux%ux%u", 512 cylinders, heads, sectors); 513 stat_end(5); 514 } else { 515 if (alt_heads == 0) { 516 alt_cyls= cylinders; 517 alt_heads= heads; 518 alt_secs= sectors; 519 } 520 521 if (heads != alt_heads || sectors != alt_secs) { 522 stat_start(1); 523 printf("WARNING:"); 524 stat_end(10); 525 stat_start(0); 526 printf( 527 "The %ux%ux%u geometry obtained from the device driver does not match", 528 cylinders, heads, sectors); 529 stat_end(10); 530 stat_start(0); 531 printf( 532 "the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch", 533 alt_cyls, alt_heads, alt_secs); 534 stat_end(10); 535 stat_start(0); 536 printf( 537 "between the two geometries to see what is best. Note that the geometry"); 538 stat_end(10); 539 stat_start(0); 540 printf( 541 "must be correct when the table is written or the system may not boot!"); 542 stat_end(10); 543 } 544 } 545 546 /* Show the base and size of the device instead of the whole drive. 547 * This makes sense for subpartitioning primary partitions. 548 */ 549 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) { 550 table[0].lowsec= (unsigned long)(geometry.base / SECTOR_SIZE); 551 table[0].size = (unsigned long)(geometry.size / SECTOR_SIZE); 552 } else { 553 precise= 0; 554 } 555 recompute0(); 556 sort(); 557 } 558 559 typedef struct indicators { /* Partition type to partition name. */ 560 unsigned char ind; 561 char name[10]; 562 } indicators_t; 563 564 indicators_t ind_table[]= { 565 { 0x00, "None" }, 566 { 0x01, "FAT-12" }, 567 { 0x02, "XENIX /" }, 568 { 0x03, "XENIX usr" }, 569 { 0x04, "FAT-16" }, 570 { 0x05, "EXTENDED" }, 571 { 0x06, "FAT-16" }, 572 { 0x07, "HPFS/NTFS" }, 573 { 0x08, "AIX" }, 574 { 0x09, "COHERENT" }, 575 { 0x0A, "OS/2" }, 576 { 0x0B, "FAT-32" }, 577 { 0x0C, "FAT?" }, 578 { 0x0E, "FAT?" }, 579 { 0x0F, "EXTENDED" }, 580 { 0x10, "OPUS" }, 581 { 0x40, "VENIX286" }, 582 { 0x42, "W2000 Dyn" }, 583 { 0x52, "MICROPORT" }, 584 { 0x63, "386/IX" }, 585 { 0x64, "NOVELL286" }, 586 { 0x65, "NOVELL386" }, 587 { 0x75, "PC/IX" }, 588 { 0x80, "MINIX-OLD" }, 589 { 0x81, "MINIX" }, 590 { 0x82, "LINUXswap" }, 591 { 0x83, "LINUX" }, 592 { 0x93, "AMOEBA" }, 593 { 0x94, "AMOEBAbad" }, 594 { 0xA5, "386BSD" }, 595 { 0xB7, "BSDI" }, 596 { 0xB8, "BSDI swap" }, 597 { 0xC7, "SYRINX" }, 598 { 0xDB, "CPM" }, 599 { 0xFF, "BADBLOCKS" }, 600 }; 601 602 char *typ2txt(int ind) 603 /* Translate a numeric partition indicator for human eyes. */ 604 { 605 indicators_t *pind; 606 607 for (pind= ind_table; pind < arraylimit(ind_table); pind++) { 608 if (pind->ind == ind) return pind->name; 609 } 610 return ""; 611 } 612 613 int round_sysind(int ind, int delta) 614 /* Find the next known partition type starting with ind in direction delta. */ 615 { 616 indicators_t *pind; 617 618 ind= (ind + delta) & 0xFF; 619 620 if (delta < 0) { 621 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {} 622 } else { 623 for (pind= ind_table; pind->ind < ind; pind++) {} 624 } 625 return pind->ind; 626 } 627 628 /* Objects on the screen, either simple pieces of the text or the cylinder 629 * number of the start of partition three. 630 */ 631 typedef enum objtype { 632 O_INFO, O_TEXT, O_DEV, O_SUB, 633 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX, 634 O_CYL, O_HEAD, O_SEC, 635 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB 636 } objtype_t; 637 638 #define rjust(type) ((type) >= O_TYPHEX) 639 #define computed(type) ((type) >= O_TYPTXT) 640 641 typedef struct object { 642 struct object *next; 643 objtype_t type; /* Text field, cylinder number, etc. */ 644 char flags; /* Modifiable? */ 645 char row; 646 char col; 647 char len; 648 struct part_entry *entry; /* What does the object refer to? */ 649 char *text; 650 char value[20]; /* Value when printed. */ 651 } object_t; 652 653 #define OF_MOD 0x01 /* Object value is modifiable. */ 654 #define OF_ODD 0x02 /* It has a somewhat odd value. */ 655 #define OF_BAD 0x04 /* Its value is no good at all. */ 656 657 /* Events: (Keypress events are the value of the key pressed.) */ 658 #define E_ENTER (-1) /* Cursor moves onto object. */ 659 #define E_LEAVE (-2) /* Cursor leaves object. */ 660 #define E_WRITE (-3) /* Write, but not by typing 'w'. */ 661 662 /* The O_SIZE objects have a dual identity. */ 663 enum howend { SIZE, LAST } howend= SIZE; 664 665 object_t *world= nil; 666 object_t *curobj= nil; 667 668 object_t *newobject(objtype_t type, int flags, int row, int col, int len) 669 /* Make a new object given a type, flags, position and length on the screen. */ 670 { 671 object_t *new; 672 object_t **aop= &world; 673 674 new= alloc(sizeof(*new)); 675 676 new->type= type; 677 new->flags= flags; 678 new->row= row; 679 new->col= col; 680 new->len= len; 681 new->entry= nil; 682 new->text= ""; 683 new->value[0]= 0; 684 685 new->next= *aop; 686 *aop= new; 687 688 return new; 689 } 690 691 unsigned long entry2base(struct part_entry *pe) 692 /* Return the base sector of the partition if defined. */ 693 { 694 return pe->sysind == NO_PART ? 0 : pe->lowsec; 695 } 696 697 unsigned long entry2last(struct part_entry *pe) 698 { 699 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1; 700 } 701 702 unsigned long entry2size(struct part_entry *pe) 703 { 704 return pe->sysind == NO_PART ? 0 : pe->size; 705 } 706 707 int overlap(unsigned long sec) 708 /* See if sec is part of another partition. */ 709 { 710 struct part_entry *pe; 711 712 for (pe= table + 1; pe <= table + NR_PARTITIONS; pe++) { 713 if (pe->sysind == NO_PART) continue; 714 715 if (pe->lowsec < sec && sec < pe->lowsec + pe->size) 716 return 1; 717 } 718 return 0; 719 } 720 721 int aligned(unsigned long sec, unsigned unit) 722 /* True if sec is aligned to unit or if it is no problem if it is unaligned. */ 723 { 724 return (offset != 0 && extbase == 0) || (sec % unit == 0); 725 } 726 727 void print(object_t *op) 728 /* Print an object's value if it changed. */ 729 { 730 struct part_entry *pe= op->entry; 731 int n; 732 unsigned long t; 733 char *name; 734 int oldflags; 735 char oldvalue[20]; 736 737 /* Remember the old flags and value. */ 738 oldflags= op->flags; 739 strcpy(oldvalue, op->value); 740 741 op->flags&= ~(OF_ODD | OF_BAD); 742 743 switch (op->type) { 744 case O_INFO: { 745 /* Current field. */ 746 static struct field { int type; char *name; } fields[]= { 747 { O_DEV, "Select device" }, 748 { O_NUM, "Active flag" }, 749 { O_TYPHEX, "Hex partition type" }, 750 { O_TYPTXT, "Partition type" }, 751 { O_SCYL, "Start cylinder" }, 752 { O_SHEAD, "Start head" }, 753 { O_SSEC, "Start sector" }, 754 { O_CYL, "Number of cylinders" }, 755 { O_HEAD, "Number of heads" }, 756 { O_SEC, "Sectors per track" }, 757 { O_LCYL, "Last cylinder" }, 758 { O_LHEAD, "Last head" }, 759 { O_LSEC, "Last sector" }, 760 { O_BASE, "Base sector" }, 761 { O_SIZE, "Size in sectors" }, 762 { O_KB, "Size in kilobytes" }, 763 { -1, "?" }, 764 }; 765 struct field *fp= fields; 766 767 while (fp->type >= 0 && fp->type != curobj->type) fp++; 768 strcpy(op->value, fp->name); 769 op->flags|= OF_ODD; 770 break; } 771 case O_TEXT: 772 /* Simple text field. */ 773 strcpy(op->value, op->text); 774 break; 775 case O_DEV: 776 case O_SUB: 777 /* Name of currently edited device. */ 778 name= op->type == O_DEV ? curdev->name : 779 offset == 0 ? "" : curdev->subname; 780 if ((n= strlen(name)) < op->len) n= op->len; 781 strcpy(op->value, name + (n - op->len)); 782 if (device < 0 && op->type == O_DEV) op->flags|= OF_BAD; 783 break; 784 case O_NUM: 785 /* Position and active flag. */ 786 sprintf(op->value, "%d%c", (int) (pe - table - 1), 787 pe->bootind & ACTIVE_FLAG ? '*' : ' '); 788 break; 789 case O_SORT: 790 /* Position if the driver sorts the table. */ 791 sprintf(op->value, "%s%d", 792 curdev->parttype >= PRIMARY ? "p" : 793 curdev->parttype == SUBPART ? "s" : "", 794 (curdev->parttype == SUBPART || 795 curdev->parttype == FLOPPY ? pe - table 796 : sort_index[pe - table]) - 1); 797 break; 798 case O_TYPHEX: 799 /* Hex partition type indicator. */ 800 sprintf(op->value, "%02X", pe->sysind); 801 break; 802 case O_TYPTXT: 803 /* Ascii partition type indicator. */ 804 strcpy(op->value, typ2txt(pe->sysind)); 805 break; 806 case O_SCYL: 807 /* Partition's start cylinder. */ 808 sprintf(op->value, "%lu", entry2base(pe) / secpcyl); 809 break; 810 case O_SHEAD: 811 /* Start head. */ 812 t= entry2base(pe); 813 sprintf(op->value, "%lu", t % secpcyl / sectors); 814 if (!aligned(t, secpcyl) && t != table[0].lowsec + sectors) 815 op->flags|= OF_ODD; 816 break; 817 case O_SSEC: 818 /* Start sector. */ 819 t= entry2base(pe); 820 sprintf(op->value, "%lu", t % sectors); 821 if (!aligned(t, sectors)) op->flags|= OF_ODD; 822 break; 823 case O_CYL: 824 /* Number of cylinders. */ 825 sprintf(op->value, "%u", cylinders); 826 break; 827 case O_HEAD: 828 /* Number of heads. */ 829 sprintf(op->value, "%u", heads); 830 break; 831 case O_SEC: 832 /* Number of sectors per track. */ 833 sprintf(op->value, "%u", sectors); 834 break; 835 case O_LCYL: 836 /* Partition's last cylinder. */ 837 t= entry2last(pe); 838 sprintf(op->value, "%lu", t == -1 ? 0 : t / secpcyl); 839 break; 840 case O_LHEAD: 841 /* Partition's last head. */ 842 t= entry2last(pe); 843 sprintf(op->value, "%lu", t == -1 ? 0 : t % secpcyl / sectors); 844 if (!aligned(t + 1, secpcyl)) op->flags|= OF_ODD; 845 break; 846 case O_LSEC: 847 /* Partition's last sector. */ 848 t= entry2last(pe); 849 if (t == -1) strcpy(op->value, "-1"); 850 else sprintf(op->value, "%lu", t % sectors); 851 if (!aligned(t + 1, sectors)) op->flags|= OF_ODD; 852 break; 853 case O_BASE: 854 /* Partition's base sector. */ 855 sprintf(op->value, "%lu", entry2base(pe)); 856 if (pe->sysind != NO_PART && pe != &table[0] 857 && (pe->lowsec <= table[0].lowsec || overlap(pe->lowsec))) 858 op->flags|= OF_BAD; 859 break; 860 case O_SIZE: 861 /* Size of partitition in sectors. */ 862 t= howend == SIZE ? entry2size(pe) : entry2last(pe); 863 sprintf(op->value, "%lu", pe->sysind == NO_PART ? 0 : t); 864 if (pe->sysind != NO_PART && (pe->size == 0 865 || pe->lowsec + pe->size > table[0].lowsec + table[0].size 866 || overlap(pe->lowsec + pe->size))) 867 op->flags|= OF_BAD; 868 break; 869 case O_KB: 870 /* Size of partitition in kilobytes. */ 871 sprintf(op->value, "%lu", entry2size(pe) / 2); 872 break; 873 default: 874 sprintf(op->value, "?? %d ??", op->type); 875 } 876 877 if (device < 0 && computed(op->type)) strcpy(op->value, "?"); 878 879 /* If a value overflows the print field then show a blank 880 * reverse video field. 881 */ 882 if ((n= strlen(op->value)) > op->len) { 883 n= 0; 884 op->flags|= OF_BAD; 885 } 886 887 /* Right or left justified? */ 888 if (rjust(op->type)) { 889 memmove(op->value + (op->len - n), op->value, n); 890 memset(op->value, ' ', op->len - n); 891 } else { 892 memset(op->value + n, ' ', op->len - n); 893 } 894 op->value[(int) op->len]= 0; 895 896 if ((op->flags & (OF_ODD | OF_BAD)) == (oldflags & (OF_ODD | OF_BAD)) 897 && strcmp(op->value, oldvalue) == 0) { 898 /* The value did not change. */ 899 return; 900 } 901 902 set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col); 903 904 if (op->flags & OF_BAD) tputs(t_so, 1, putchr); 905 else 906 if (op->flags & OF_ODD) tputs(t_md, 1, putchr); 907 putstr(op->value); 908 if (op->flags & OF_BAD) tputs(t_se, 1, putchr); 909 else 910 if (op->flags & OF_ODD) tputs(t_me, 1, putchr); 911 } 912 913 void display(void) 914 /* Repaint all objects that changed. */ 915 { 916 object_t *op; 917 918 for (op= world; op != nil; op= op->next) print(op); 919 } 920 921 int typing; /* Set if a digit has been typed to set a value. */ 922 int magic; /* Changes when using the magic key. */ 923 924 void event(int ev, object_t *op); 925 926 void m_redraw(int ev, object_t *op) 927 /* Redraw the screen. */ 928 { 929 object_t *op2; 930 931 if (ev != ctrl('L')) return; 932 933 clear_screen(); 934 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0; 935 } 936 937 void m_toggle(int ev, object_t *op) 938 /* Toggle between the driver and alternate geometry. */ 939 { 940 unsigned t; 941 942 if (ev != 'X') return; 943 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors) 944 return; 945 946 t= cylinders; cylinders= alt_cyls; alt_cyls= t; 947 t= heads; heads= alt_heads; alt_heads= t; 948 t= sectors; sectors= alt_secs; alt_secs= t; 949 dirty= 1; 950 recompute0(); 951 } 952 953 char size_last[]= "Size"; 954 955 void m_orientation(int ev, object_t *op) 956 { 957 if (ev != ' ') return; 958 959 switch (howend) { 960 case SIZE: 961 howend= LAST; 962 strcpy(size_last, "Last"); 963 break; 964 case LAST: 965 howend= SIZE; 966 strcpy(size_last, "Size"); 967 } 968 } 969 970 void m_move(int ev, object_t *op) 971 /* Move to the nearest modifiably object in the intended direction. Objects 972 * on the same row or column are really near. 973 */ 974 { 975 object_t *near, *op2; 976 unsigned dist, d2, dr, dc; 977 978 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H') 979 return; 980 981 if (device < 0) { 982 /* No device open? Then try to read first. */ 983 event('r', op); 984 if (device < 0) return; 985 } 986 987 near= op; 988 dist= -1; 989 990 for (op2= world; op2 != nil; op2= op2->next) { 991 if (op2 == op || !(op2->flags & OF_MOD)) continue; 992 993 dr= abs(op2->row - op->row); 994 dc= abs(op2->col - op->col); 995 996 d2= 25*dr*dr + dc*dc; 997 if (op2->row != op->row && op2->col != op->col) d2+= 1000; 998 999 switch (ev) { 1000 case 'h': /* Left */ 1001 if (op2->col >= op->col) d2= -1; 1002 break; 1003 case 'j': /* Down */ 1004 if (op2->row <= op->row) d2= -1; 1005 break; 1006 case 'k': /* Up */ 1007 if (op2->row >= op->row) d2= -1; 1008 break; 1009 case 'l': /* Right */ 1010 if (op2->col <= op->col) d2= -1; 1011 break; 1012 case 'H': /* Home */ 1013 if (op2->type == O_DEV) d2= 0; 1014 } 1015 if (d2 < dist) { near= op2; dist= d2; } 1016 } 1017 if (near != op) event(E_LEAVE, op); 1018 event(E_ENTER, near); 1019 } 1020 1021 void m_updown(int ev, object_t *op) 1022 /* Move a partition table entry up or down. */ 1023 { 1024 int i, j; 1025 struct part_entry tmp; 1026 int tmpx; 1027 1028 if (ev != ctrl('K') && ev != ctrl('J')) return; 1029 if (op->entry == nil) return; 1030 1031 i= op->entry - table; 1032 if (ev == ctrl('K')) { 1033 if (i <= 1) return; 1034 j= i-1; 1035 } else { 1036 if (i >= NR_PARTITIONS) return; 1037 j= i+1; 1038 } 1039 1040 tmp= table[i]; table[i]= table[j]; table[j]= tmp; 1041 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx; 1042 sort(); 1043 dirty= 1; 1044 event(ev == ctrl('K') ? 'k' : 'j', op); 1045 } 1046 1047 void m_enter(int ev, object_t *op) 1048 /* We've moved onto this object. */ 1049 { 1050 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X') 1051 return; 1052 curobj= op; 1053 typing= 0; 1054 magic= 0; 1055 } 1056 1057 void m_leave(int ev, object_t *op) 1058 /* About to leave this object. */ 1059 { 1060 if (ev != E_LEAVE) return; 1061 } 1062 1063 int within(unsigned *var, unsigned low, unsigned value, unsigned high) 1064 /* Only set *var to value if it looks reasonable. */ 1065 { 1066 if (low <= value && value <= high) { 1067 *var= value; 1068 return 1; 1069 } else 1070 return 0; 1071 } 1072 1073 int lwithin(unsigned long *var, unsigned long low, unsigned long value, 1074 unsigned long high) 1075 { 1076 if (low <= value && value <= high) { 1077 *var= value; 1078 return 1; 1079 } else 1080 return 0; 1081 } 1082 1083 int nextdevice(object_t *op, int delta) 1084 /* Select the next or previous device from the device list. */ 1085 { 1086 dev_t rdev; 1087 1088 if (offset != 0) return 0; 1089 if (dirty) event(E_WRITE, op); 1090 if (dirty) return 0; 1091 1092 if (device >= 0) { 1093 (void) close(device); 1094 device= -1; 1095 } 1096 recompute0(); 1097 1098 rdev= curdev->rdev; 1099 if (delta < 0) { 1100 do 1101 curdev= curdev->prev; 1102 while (delta < -1 && major(curdev->rdev) == major(rdev) 1103 && curdev->rdev < rdev); 1104 } else { 1105 do 1106 curdev= curdev->next; 1107 while (delta > 1 && major(curdev->rdev) == major(rdev) 1108 && curdev->rdev > rdev); 1109 } 1110 return 1; 1111 } 1112 1113 void check_ind(struct part_entry *pe) 1114 /* If there are no other partitions then make this new one active. */ 1115 { 1116 struct part_entry *pe2; 1117 1118 if (pe->sysind != NO_PART) return; 1119 1120 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++) 1121 if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break; 1122 1123 if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG; 1124 } 1125 1126 int check_existing(struct part_entry *pe) 1127 /* Check and if not ask if an existing partition may be modified. */ 1128 { 1129 static int expert= 0; 1130 int c; 1131 1132 if (expert || pe == nil || !existing[pe - table]) return 1; 1133 1134 stat_start(1); 1135 putstr("Do you wish to modify existing partitions? (y/n) "); 1136 fflush(stdout); 1137 while ((c= getchar()) != 'y' && c != 'n') {} 1138 putchr(c); 1139 stat_end(3); 1140 return (expert= (c == 'y')); 1141 } 1142 1143 void m_modify(int ev, object_t *op) 1144 /* Increment, decrement, set, or toggle the value of an object, using 1145 * arithmetic tricks the author doesn't understand either. 1146 */ 1147 { 1148 object_t *op2; 1149 struct part_entry *pe= op->entry; 1150 int mul, delta; 1151 unsigned level= 1; 1152 unsigned long surplus; 1153 int radix= op->type == O_TYPHEX ? 0x10 : 10; 1154 unsigned long t; 1155 1156 if (device < 0 && op->type != O_DEV) return; 1157 1158 switch (ev) { 1159 case '-': 1160 mul= radix; delta= -1; typing= 0; 1161 break; 1162 case '+': 1163 mul= radix; delta= 1; typing= 0; 1164 break; 1165 case '\b': 1166 if (!typing) return; 1167 mul= 1; delta= 0; 1168 break; 1169 case '\r': 1170 typing= 0; 1171 return; 1172 default: 1173 if ('0' <= ev && ev <= '9') 1174 delta= ev - '0'; 1175 else 1176 if (radix == 0x10 && 'a' <= ev && ev <= 'f') 1177 delta= ev - 'a' + 10; 1178 else 1179 if (radix == 0x10 && 'A' <= ev && ev <= 'F') 1180 delta= ev - 'A' + 10; 1181 else 1182 return; 1183 1184 mul= typing ? radix*radix : 0; 1185 typing= 1; 1186 } 1187 magic= 0; 1188 1189 if (!check_existing(pe)) return; 1190 1191 switch (op->type) { 1192 case O_DEV: 1193 if (ev != '-' && ev != '+') return; 1194 if (!nextdevice(op, delta)) return; 1195 break; 1196 case O_CYL: 1197 if (!within(&cylinders, 1, 1198 cylinders * mul / radix + delta, 1024)) return; 1199 recompute0(); 1200 break; 1201 case O_HEAD: 1202 if (!within(&heads, 1, heads * mul / radix + delta, 255)) 1203 return; 1204 recompute0(); 1205 break; 1206 case O_SEC: 1207 if (!within(§ors, 1, sectors * mul / radix + delta, 63)) 1208 return; 1209 recompute0(); 1210 break; 1211 case O_NUM: 1212 if (ev != '-' && ev != '+') return; 1213 for (op2= world; op2 != nil; op2= op2->next) { 1214 if (op2->type == O_NUM && ev == '+') 1215 op2->entry->bootind= 0; 1216 } 1217 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0; 1218 break; 1219 case O_TYPHEX: 1220 check_ind(pe); 1221 pe->sysind= pe->sysind * mul / radix + delta; 1222 break; 1223 case O_TYPTXT: 1224 if (ev != '-' && ev != '+') return; 1225 check_ind(pe); 1226 pe->sysind= round_sysind(pe->sysind, delta); 1227 break; 1228 case O_SCYL: 1229 level= heads; 1230 case O_SHEAD: 1231 level*= sectors; 1232 case O_SSEC: 1233 if (op->type != O_SCYL && ev != '-' && ev != '+') return; 1234 case O_BASE: 1235 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe)); 1236 t= pe->lowsec; 1237 surplus= t % level; 1238 if (!lwithin(&t, 0L, 1239 (t / level * mul / radix + delta) * level + surplus, 1240 MAXSIZE)) return; 1241 if (howend == LAST || op->type != O_BASE) 1242 pe->size-= t - pe->lowsec; 1243 pe->lowsec= t; 1244 check_ind(pe); 1245 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; 1246 break; 1247 case O_LCYL: 1248 level= heads; 1249 case O_LHEAD: 1250 level*= sectors; 1251 case O_LSEC: 1252 if (op->type != O_LCYL && ev != '-' && ev != '+') return; 1253 1254 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe)); 1255 t= pe->lowsec + pe->size - 1 + level; 1256 surplus= t % level - mul / radix * level; 1257 if (!lwithin(&t, 0L, 1258 (t / level * mul / radix + delta) * level + surplus, 1259 MAXSIZE)) return; 1260 pe->size= t - pe->lowsec + 1; 1261 check_ind(pe); 1262 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; 1263 break; 1264 case O_KB: 1265 level= 2; 1266 if (mul == 0) pe->size= 0; /* new value, no surplus */ 1267 case O_SIZE: 1268 if (pe->sysind == NO_PART) { 1269 if (op->type == O_KB || howend == SIZE) { 1270 /* First let loose magic to set the base. */ 1271 event('m', op); 1272 magic= 0; 1273 pe->size= 0; 1274 event(ev, op); 1275 return; 1276 } 1277 memset(pe, 0, sizeof(*pe)); 1278 } 1279 t= (op->type == O_KB || howend == SIZE) ? pe->size 1280 : pe->lowsec + pe->size - 1; 1281 surplus= t % level; 1282 if (!lwithin(&t, 0L, 1283 (t / level * mul / radix + delta) * level + surplus, 1284 MAXSIZE)) return; 1285 pe->size= (op->type == O_KB || howend == SIZE) ? t : 1286 t - pe->lowsec + 1; 1287 check_ind(pe); 1288 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; 1289 break; 1290 default: 1291 return; 1292 } 1293 1294 /* The order among the entries may have changed. */ 1295 sort(); 1296 dirty= 1; 1297 } 1298 1299 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)]; 1300 int nspells; 1301 objtype_t touching; 1302 1303 void newspell(unsigned long charm) 1304 /* Add a new spell, descending order for the base, ascending for the size. */ 1305 { 1306 int i, j; 1307 1308 if (charm - table[0].lowsec > table[0].size) return; 1309 1310 for (i= 0; i < nspells; i++) { 1311 if (charm == spell[i]) return; /* duplicate */ 1312 1313 if (touching == O_BASE) { 1314 if (charm == table[0].lowsec + table[0].size) return; 1315 if ((spell[0] - charm) < (spell[0] - spell[i])) break; 1316 } else { 1317 if (charm == table[0].lowsec) return; 1318 if ((charm - spell[0]) < (spell[i] - spell[0])) break; 1319 } 1320 } 1321 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1]; 1322 spell[i]= charm; 1323 } 1324 1325 void m_magic(int ev, object_t *op) 1326 /* Apply magic onto a base or size number. */ 1327 { 1328 struct part_entry *pe= op->entry, *pe2; 1329 int rough= (offset != 0 && extbase == 0); 1330 1331 if (ev != 'm' || device < 0) return; 1332 typing= 0; 1333 1334 if (!check_existing(pe)) return; 1335 1336 if (magic == 0) { 1337 /* See what magic we can let loose on this value. */ 1338 nspells= 1; 1339 1340 /* First spell, the current value. */ 1341 switch (op->type) { 1342 case O_SCYL: 1343 case O_SHEAD: /* Start of partition. */ 1344 case O_SSEC: 1345 case O_BASE: 1346 touching= O_BASE; 1347 spell[0]= pe->lowsec; 1348 break; 1349 case O_LCYL: 1350 case O_LHEAD: 1351 case O_LSEC: /* End of partition. */ 1352 case O_KB: 1353 case O_SIZE: 1354 touching= O_SIZE; 1355 spell[0]= pe->lowsec + pe->size; 1356 break; 1357 default: 1358 return; 1359 } 1360 if (pe->sysind == NO_PART) { 1361 memset(pe, 0, sizeof(*pe)); 1362 check_ind(pe); 1363 pe->sysind= MINIX_PART; 1364 spell[0]= 0; 1365 if (touching == O_SIZE) { 1366 /* First let loose magic on the base. */ 1367 object_t *op2; 1368 1369 for (op2= world; op2 != nil; op2= op2->next) { 1370 if (op2->row == op->row && 1371 op2->type == O_BASE) { 1372 event('m', op2); 1373 } 1374 } 1375 magic= 0; 1376 event('m', op); 1377 return; 1378 } 1379 } 1380 /* Avoid the first sector on the device. */ 1381 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1); 1382 1383 /* Further interesting values are the the bases of other 1384 * partitions or their ends. 1385 */ 1386 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) { 1387 if (pe2 == pe || pe2->sysind == NO_PART) continue; 1388 if (pe2->lowsec == table[0].lowsec) 1389 newspell(table[0].lowsec + 1); 1390 else 1391 newspell(pe2->lowsec); 1392 newspell(pe2->lowsec + pe2->size); 1393 if (touching == O_BASE && howend == SIZE) { 1394 newspell(pe2->lowsec - pe->size); 1395 newspell(pe2->lowsec + pe2->size - pe->size); 1396 } 1397 if (pe2->lowsec % sectors != 0) rough= 1; 1398 } 1399 /* Present values rounded up to the next cylinder unless 1400 * the table is already a mess. Use "start + 1 track" instead 1401 * of "start + 1 cylinder". Also add the end of the last 1402 * cylinder. 1403 */ 1404 if (!rough) { 1405 unsigned long n= spell[0]; 1406 if (n == table[0].lowsec) n++; 1407 n= (n + sectors - 1) / sectors * sectors; 1408 if (n != table[0].lowsec + sectors) 1409 n= (n + secpcyl - 1) / secpcyl * secpcyl; 1410 newspell(n); 1411 if (touching == O_SIZE) 1412 newspell(table[0].size / secpcyl * secpcyl); 1413 } 1414 } 1415 /* Magic has been applied, a spell needs to be chosen. */ 1416 1417 if (++magic == nspells) magic= 0; 1418 1419 if (touching == O_BASE) { 1420 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec; 1421 pe->lowsec= spell[magic]; 1422 } else 1423 pe->size= spell[magic] - pe->lowsec; 1424 1425 /* The order among the entries may have changed. */ 1426 sort(); 1427 dirty= 1; 1428 } 1429 1430 typedef struct diving { 1431 struct diving *up; 1432 struct part_entry old0; 1433 char *oldsubname; 1434 parttype_t oldparttype; 1435 unsigned long oldoffset; 1436 unsigned long oldextbase; 1437 } diving_t; 1438 1439 diving_t *diving= nil; 1440 1441 void m_in(int ev, object_t *op) 1442 /* Go down into a primary or extended partition. */ 1443 { 1444 diving_t *newdiv; 1445 struct part_entry *pe= op->entry, ext; 1446 int n; 1447 1448 if (ev != '>' || device < 0 || pe == nil || pe == &table[0] 1449 || (!(pe->sysind == MINIX_PART && offset == 0) 1450 && !ext_part(pe->sysind)) 1451 || pe->size == 0) return; 1452 1453 ext= *pe; 1454 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec; 1455 1456 if (dirty) event(E_WRITE, op); 1457 if (dirty) return; 1458 if (device >= 0) { close(device); device= -1; } 1459 1460 newdiv= alloc(sizeof(*newdiv)); 1461 newdiv->old0= table[0]; 1462 newdiv->oldsubname= curdev->subname; 1463 newdiv->oldparttype= curdev->parttype; 1464 newdiv->oldoffset= offset; 1465 newdiv->oldextbase= extbase; 1466 newdiv->up= diving; 1467 diving= newdiv; 1468 1469 table[0]= ext; 1470 1471 n= strlen(diving->oldsubname); 1472 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0])); 1473 strcpy(curdev->subname, diving->oldsubname); 1474 curdev->subname[n++]= ':'; 1475 curdev->subname[n++]= '0' + (pe - table - 1); 1476 curdev->subname[n]= 0; 1477 1478 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO; 1479 offset= ext.lowsec; 1480 if (ext_part(ext.sysind) && extbase == 0) { 1481 extbase= ext.lowsec; 1482 extsize= ext.size; 1483 curdev->parttype= DUNNO; 1484 } 1485 1486 submerged= 1; 1487 event('r', op); 1488 } 1489 1490 void m_out(int ev, object_t *op) 1491 /* Go up from an extended or subpartition table to its enclosing. */ 1492 { 1493 diving_t *olddiv; 1494 1495 if (ev != '<' || diving == nil) return; 1496 1497 if (dirty) event(E_WRITE, op); 1498 if (dirty) return; 1499 if (device >= 0) { close(device); device= -1; } 1500 1501 olddiv= diving; 1502 diving= olddiv->up; 1503 1504 table[0]= olddiv->old0; 1505 1506 free(curdev->subname); 1507 curdev->subname= olddiv->oldsubname; 1508 1509 curdev->parttype= olddiv->oldparttype; 1510 offset= olddiv->oldoffset; 1511 extbase= olddiv->oldextbase; 1512 1513 free(olddiv); 1514 1515 event('r', op); 1516 if (diving == nil) submerged= 0; /* We surfaced. */ 1517 } 1518 1519 void installboot(unsigned char *bootblock, char *masterboot) 1520 /* Install code from a master bootstrap into a boot block. */ 1521 { 1522 FILE *mfp; 1523 unsigned char buf[SECTOR_SIZE]; 1524 int n; 1525 char *err; 1526 1527 if ((mfp= fopen(masterboot, "r")) == nil) { 1528 err= strerror(errno); 1529 goto m_err; 1530 } 1531 1532 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp); 1533 if (ferror(mfp)) { 1534 err= strerror(errno); 1535 fclose(mfp); 1536 goto m_err; 1537 } 1538 else if (n < 256) { 1539 err= "Is probably not a boot sector, too small"; 1540 fclose(mfp); 1541 goto m_err; 1542 } 1543 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) { 1544 /* if only code, it cannot override partition table */ 1545 err= "Does not fit in a boot sector"; 1546 fclose(mfp); 1547 goto m_err; 1548 } 1549 else if (n == SECTOR_SIZE) { 1550 if (buf[510] != 0x55 || buf[511] != 0xaa) { 1551 err= "Is not a boot sector (bad magic)"; 1552 fclose(mfp); 1553 goto m_err; 1554 } 1555 n = PART_TABLE_OFF; 1556 } 1557 1558 if (n > PART_TABLE_OFF) { 1559 err= "Does not fit in a boot sector"; 1560 fclose(mfp); 1561 goto m_err; 1562 } 1563 1564 memcpy(bootblock, buf, n); 1565 fclose(mfp); 1566 1567 /* Bootstrap installed. */ 1568 return; 1569 1570 m_err: 1571 stat_start(1); 1572 printf("%s: %s", masterboot, err); 1573 stat_end(5); 1574 } 1575 1576 ssize_t boot_readwrite(int rw) 1577 /* Read (0) or write (1) the boot sector. */ 1578 { 1579 int r = 0; 1580 1581 if (lseek(device, (off_t)offset * SECTOR_SIZE, SEEK_SET) < 0) 1582 return -1; 1583 1584 switch (rw) { 1585 case 0: r= read(device, bootblock, SECTOR_SIZE); break; 1586 case 1: r= write(device, bootblock, SECTOR_SIZE); break; 1587 } 1588 1589 return r; 1590 } 1591 1592 void m_read(int ev, object_t *op) 1593 /* Read the partition table from the current device. */ 1594 { 1595 int i, mode, n; 1596 struct part_entry *pe; 1597 1598 if (ev != 'r' || device >= 0) return; 1599 1600 /* Open() may cause kernel messages: */ 1601 stat_start(0); 1602 fflush(stdout); 1603 1604 if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0 1605 && (errno != EACCES 1606 || (device= open(curdev->name, mode= O_RDONLY)) < 0)) 1607 ) { 1608 stat_start(1); 1609 printf("%s: %s", curdev->name, strerror(errno)); 1610 stat_end(5); 1611 if (device >= 0) { close(device); device= -1; } 1612 return; 1613 } 1614 1615 /* Assume up to five lines of kernel messages. */ 1616 statusrow+= 5-1; 1617 stat_end(5); 1618 1619 if (mode == O_RDONLY) { 1620 stat_start(1); 1621 printf("%s: Readonly", curdev->name); 1622 stat_end(5); 1623 } 1624 memset(bootblock, 0, sizeof(bootblock)); 1625 1626 n= boot_readwrite(0); 1627 1628 if (n <= 0) stat_start(1); 1629 if (n < 0) { 1630 printf("%s: %s", curdev->name, strerror(errno)); 1631 close(device); 1632 device= -1; 1633 } else 1634 if (n < SECTOR_SIZE) printf("%s: Unexpected EOF", curdev->subname); 1635 if (n <= 0) stat_end(5); 1636 1637 if (n < SECTOR_SIZE) n= SECTOR_SIZE; 1638 1639 memcpy(table+1, bootblock+PART_TABLE_OFF, 1640 NR_PARTITIONS * sizeof(table[1])); 1641 for (i= 1; i <= NR_PARTITIONS; i++) { 1642 if ((table[i].bootind & ~ACTIVE_FLAG) != 0) break; 1643 } 1644 if (i <= NR_PARTITIONS || bootblock[510] != 0x55 1645 || bootblock[511] != 0xAA) { 1646 /* Invalid boot block, install bootstrap, wipe partition table. 1647 */ 1648 memset(bootblock, 0, sizeof(bootblock)); 1649 installboot(bootblock, MASTERBOOT); 1650 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1])); 1651 stat_start(1); 1652 printf("%s: Invalid partition table (reset)", curdev->subname); 1653 stat_end(5); 1654 } 1655 1656 /* Fix an extended partition table up to something mere mortals can 1657 * understand. Record already defined partitions. 1658 */ 1659 for (i= 1; i <= NR_PARTITIONS; i++) { 1660 pe= &table[i]; 1661 if (extbase != 0 && pe->sysind != NO_PART) 1662 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset; 1663 existing[i]= pe->sysind != NO_PART; 1664 } 1665 geometry(); 1666 dirty= 0; 1667 1668 /* Warn about grave dangers ahead. */ 1669 if (extbase != 0) { 1670 stat_start(1); 1671 printf("Warning: You are in an extended partition."); 1672 stat_end(5); 1673 } 1674 } 1675 1676 void m_write(int ev, object_t *op) 1677 /* Write the partition table back if modified. */ 1678 { 1679 int c; 1680 struct part_entry new_table[NR_PARTITIONS], *pe; 1681 1682 if (ev != 'w' && ev != E_WRITE) return; 1683 if (device < 0) { dirty= 0; return; } 1684 if (!dirty) { 1685 if (ev == 'w') { 1686 stat_start(1); 1687 printf("%s is not changed, or has already been written", 1688 curdev->subname); 1689 stat_end(2); 1690 } 1691 return; 1692 } 1693 1694 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) { 1695 /* Invalid boot block, warn user. */ 1696 stat_start(1); 1697 printf("Warning: About to write a new table on %s", 1698 curdev->subname); 1699 stat_end(5); 1700 } 1701 if (extbase != 0) { 1702 /* Will this stop the luser? Probably not... */ 1703 stat_start(1); 1704 printf("You have changed an extended partition. Bad Idea."); 1705 stat_end(5); 1706 } 1707 stat_start(1); 1708 putstr("Save partition table? (y/n) "); 1709 fflush(stdout); 1710 1711 while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {} 1712 1713 if (c == ctrl('?')) putstr("DEL"); else putchr(c); 1714 stat_end(5); 1715 if (c == 'n' && ev == E_WRITE) dirty= 0; 1716 if (c != 'y') return; 1717 1718 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1])); 1719 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) { 1720 if (pe->sysind == NO_PART) { 1721 memset(pe, 0, sizeof(*pe)); 1722 } else { 1723 abs2dos(&pe->start_head, pe->lowsec); 1724 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1); 1725 1726 /* Fear and loathing time: */ 1727 if (extbase != 0) 1728 pe->lowsec-= ext_part(pe->sysind) 1729 ? extbase : offset; 1730 } 1731 } 1732 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table)); 1733 bootblock[510]= 0x55; 1734 bootblock[511]= 0xAA; 1735 1736 if (boot_readwrite(1) < 0) { 1737 stat_start(1); 1738 printf("%s: %s", curdev->name, strerror(errno)); 1739 stat_end(5); 1740 return; 1741 } 1742 dirty= 0; 1743 } 1744 1745 void m_shell(int ev, object_t *op) 1746 /* Shell escape, to do calculations for instance. */ 1747 { 1748 int r, pid, status; 1749 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int); 1750 1751 if (ev != 's') return; 1752 1753 reset_tty(); 1754 fflush(stdout); 1755 1756 switch (pid= fork()) { 1757 case -1: 1758 stat_start(1); 1759 printf("can't fork: %s\n", strerror(errno)); 1760 stat_end(3); 1761 break; 1762 case 0: 1763 if (device >= 0) (void) close(device); 1764 execl("/bin/sh", "sh", (char *) nil); 1765 r= errno; 1766 stat_start(1); 1767 printf("/bin/sh: %s\n", strerror(errno)); 1768 stat_end(3); 1769 exit(127); 1770 } 1771 sigint= signal(SIGINT, SIG_IGN); 1772 sigquit= signal(SIGQUIT, SIG_IGN); 1773 sigterm= signal(SIGTERM, SIG_IGN); 1774 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {} 1775 (void) signal(SIGINT, sigint); 1776 (void) signal(SIGQUIT, sigquit); 1777 (void) signal(SIGTERM, sigterm); 1778 tty_raw(); 1779 if (pid < 0) 1780 ; 1781 else 1782 if (WIFEXITED(status) && WEXITSTATUS(status) == 127) 1783 stat_start(0); /* Match the stat_start in the child. */ 1784 else 1785 event(ctrl('L'), op); 1786 } 1787 1788 void m_dump(int ev, object_t *op) 1789 /* Raw dump of the partition table. */ 1790 { 1791 struct part_entry table[NR_PARTITIONS], *pe; 1792 int i; 1793 unsigned chs[3]; 1794 1795 if (ev != 'p' || device < 0) return; 1796 1797 memcpy(table, bootblock+PART_TABLE_OFF, 1798 NR_PARTITIONS * sizeof(table[0])); 1799 for (i= 0; i < NR_PARTITIONS; i++) { 1800 pe= &table[i]; 1801 stat_start(0); 1802 dos2chs(&pe->start_head, chs); 1803 printf("%2d%c %02X%15d%5d%4d", 1804 i+1, 1805 pe->bootind & ACTIVE_FLAG ? '*' : ' ', 1806 pe->sysind, 1807 chs[0], chs[1], chs[2]); 1808 dos2chs(&pe->last_head, chs); 1809 printf("%6d%5d%4d%10u%10u%9u", 1810 chs[0], chs[1], chs[2], 1811 pe->lowsec, 1812 howend == SIZE ? pe->size : pe->size + pe->lowsec - 1, 1813 pe->size / 2); 1814 stat_end(10); 1815 } 1816 stat_start(0); 1817 printf("(Raw dump of the original %.40s table)", 1818 curdev->subname); 1819 stat_end(10); 1820 } 1821 1822 int quitting= 0; 1823 1824 void m_quit(int ev, object_t *op) 1825 /* Write the partition table if modified and exit. */ 1826 { 1827 if (ev != 'q' && ev != 'x') return; 1828 1829 quitting= 1; 1830 1831 if (dirty) event(E_WRITE, op); 1832 if (dirty) quitting= 0; 1833 } 1834 1835 void m_help(int ev, object_t *op) 1836 /* For people without a clue; let's hope they can find the '?' key. */ 1837 { 1838 static struct help { 1839 char *keys; 1840 char *what; 1841 } help[]= { 1842 { "? !", "This help / more advice!" }, 1843 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" }, 1844 { "0-9 (a-f)", "Enter value" }, 1845 { "hjkl (arrow keys)", "Move around" }, 1846 { "CTRL-K CTRL-J", "Move entry up/down" }, 1847 { "CTRL-L", "Redraw screen" }, 1848 { ">", "Start a subpartition table" }, 1849 { "<", "Back to the primary partition table" }, 1850 { "m", "Cycle through magic values" }, 1851 { "spacebar", "Show \"Size\" or \"Last\"" }, 1852 { "r w", "Read/write partition table" }, 1853 { "p s q x", "Raw dump / Shell escape / Quit / Exit" }, 1854 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" }, 1855 }; 1856 static char *advice[] = { 1857 "* Choose a disk with '+' and '-', then hit 'r'.", 1858 "* To change any value: Move to it and use '+', '-' or type the desired value.", 1859 "* To make a new partition: Move over to the Size or Kb field of an unused", 1860 " partition and type the size. Hit the 'm' key to pad the partition out to", 1861 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.", 1862 " You can hit 'm' more than once on a base or size field to see several", 1863 " interesting values go by. Note: Other Operating Systems can be picky about", 1864 " partitions that are not padded to cylinder boundaries. Look for highlighted", 1865 " head or sector numbers.", 1866 "* To reuse a partition: Change the type to MINIX.", 1867 "* To delete a partition: Type a zero in the hex Type field.", 1868 "* To make a partition active: Type '+' in the Num field.", 1869 "* To study the list of keys: Type '?'.", 1870 }; 1871 1872 if (ev == '?') { 1873 struct help *hp; 1874 1875 for (hp= help; hp < arraylimit(help); hp++) { 1876 stat_start(0); 1877 printf("%-25s - %s", hp->keys, hp->what); 1878 stat_end(0); 1879 } 1880 stat_start(0); 1881 putstr("Things like "); 1882 putstr(t_so); putstr("this"); putstr(t_se); 1883 putstr(" must be checked, but "); 1884 putstr(t_md); putstr("this"); putstr(t_me); 1885 putstr(" is not really a problem"); 1886 stat_end(0); 1887 } else 1888 if (ev == '!') { 1889 char **ap; 1890 1891 for (ap= advice; ap < arraylimit(advice); ap++) { 1892 stat_start(0); 1893 putstr(*ap); 1894 stat_end(0); 1895 } 1896 } 1897 } 1898 1899 void event(int ev, object_t *op) 1900 /* Simply call all modifiers for an event, each one knows when to act. */ 1901 { 1902 m_help(ev, op); 1903 m_redraw(ev, op); 1904 m_toggle(ev, op); 1905 m_orientation(ev, op); 1906 m_move(ev, op); 1907 m_updown(ev, op); 1908 m_enter(ev, op); 1909 m_leave(ev, op); 1910 m_modify(ev, op); 1911 m_magic(ev, op); 1912 m_in(ev, op); 1913 m_out(ev, op); 1914 m_read(ev, op); 1915 m_write(ev, op); 1916 m_shell(ev, op); 1917 m_dump(ev, op); 1918 m_quit(ev, op); 1919 } 1920 1921 int keypress(void) 1922 /* Get a single keypress. Translate compound keypresses (arrow keys) to 1923 * their simpler equivalents. 1924 */ 1925 { 1926 char ch; 1927 int c; 1928 int esc= 0; 1929 1930 set_cursor(curobj->row, curobj->col); 1931 fflush(stdout); 1932 1933 do { 1934 if (read(0, &ch, sizeof(ch)) < 0) fatal("stdin"); 1935 c= (unsigned char) ch; 1936 switch (esc) { 1937 case 0: 1938 switch (c) { 1939 case ctrl('['): esc= 1; break; 1940 case '_': c= '-'; break; 1941 case '=': c= '+'; break; 1942 } 1943 break; 1944 case 1: 1945 esc= c == '[' ? 2 : 0; 1946 break; 1947 case 2: 1948 switch (c) { 1949 case 'D': c= 'h'; break; 1950 case 'B': c= 'j'; break; 1951 case 'A': c= 'k'; break; 1952 case 'C': c= 'l'; break; 1953 case 'H': c= 'H'; break; 1954 case 'U': 1955 case 'S': c= '-'; break; 1956 case 'V': 1957 case 'T': c= '+'; break; 1958 } 1959 /*FALL THROUGH*/ 1960 default: 1961 esc= 0; 1962 } 1963 } while (esc > 0); 1964 1965 switch (c) { 1966 case ctrl('B'): c= 'h'; break; 1967 case ctrl('N'): c= 'j'; break; 1968 case ctrl('P'): c= 'k'; break; 1969 case ctrl('F'): c= 'l'; break; 1970 } 1971 1972 return c; 1973 } 1974 1975 void mainloop(void) 1976 /* Get keypress, handle event, display results, reset screen, ad infinitum. */ 1977 { 1978 int key; 1979 1980 while (!quitting) { 1981 stat_reset(); 1982 1983 key= keypress(); 1984 1985 event(key, curobj); 1986 1987 display(); 1988 } 1989 } 1990 1991 int main(int argc, char **argv) 1992 { 1993 object_t *op; 1994 int i, r, key; 1995 struct part_entry *pe; 1996 1997 /* Define a few objects to show on the screen. First text: */ 1998 op= newobject(O_INFO, 0, 0, 2, 19); 1999 op= newobject(O_TEXT, 0, 0, 22, 13); op->text= "----first----"; 2000 op= newobject(O_TEXT, 0, 0, 37, 13); op->text= "--geom/last--"; 2001 op= newobject(O_TEXT, 0, 0, 52, 18); op->text= "------sectors-----"; 2002 op= newobject(O_TEXT, 0, 1, 4, 6); op->text= "Device"; 2003 op= newobject(O_TEXT, 0, 1, 23, 12); op->text= "Cyl Head Sec"; 2004 op= newobject(O_TEXT, 0, 1, 38, 12); op->text= "Cyl Head Sec"; 2005 op= newobject(O_TEXT, 0, 1, 56, 4); op->text= "Base"; 2006 op= newobject(O_TEXT, 0, 1, 66, 4); op->text= size_last; 2007 op= newobject(O_TEXT, 0, 1, 78, 2); op->text= "Kb"; 2008 op= newobject(O_TEXT, 0, 4, 0, 15); op->text= "Num Sort Type"; 2009 2010 /* The device is the current object: */ 2011 curobj= newobject(O_DEV, OF_MOD, 2, 4, 15); 2012 op= newobject(O_SUB, 0, 3, 4, 15); 2013 2014 /* Geometry: */ 2015 op= newobject(O_CYL, OF_MOD, 2, 40, 5); op->entry= &table[0]; 2016 op= newobject(O_HEAD, OF_MOD, 2, 45, 3); op->entry= &table[0]; 2017 op= newobject(O_SEC, OF_MOD, 2, 49, 2); op->entry= &table[0]; 2018 2019 /* Objects for the device: */ 2020 op= newobject(O_SCYL, 0, 3, 25, 5); op->entry= &table[0]; 2021 op= newobject(O_SHEAD, 0, 3, 30, 3); op->entry= &table[0]; 2022 op= newobject(O_SSEC, 0, 3, 34, 2); op->entry= &table[0]; 2023 op= newobject(O_LCYL, 0, 3, 40, 5); op->entry= &table[0]; 2024 op= newobject(O_LHEAD, 0, 3, 45, 3); op->entry= &table[0]; 2025 op= newobject(O_LSEC, 0, 3, 49, 2); op->entry= &table[0]; 2026 op= newobject(O_BASE, 0, 3, 59, 9); op->entry= &table[0]; 2027 op= newobject(O_SIZE, 0, 3, 69, 9); op->entry= &table[0]; 2028 op= newobject(O_KB, 0, 3, 79, 9); op->entry= &table[0]; 2029 2030 /* Objects for each partition: */ 2031 for (r= 5, pe= table+1; pe <= table+NR_PARTITIONS; r++, pe++) { 2032 op= newobject(O_NUM, OF_MOD, r, 1, 2); op->entry= pe; 2033 op= newobject(O_SORT, 0, r, 5, 2); op->entry= pe; 2034 op= newobject(O_TYPHEX, OF_MOD, r, 10, 2); op->entry= pe; 2035 op= newobject(O_TYPTXT, OF_MOD, r, 12, 9); op->entry= pe; 2036 op= newobject(O_SCYL, OF_MOD, r, 25, 5); op->entry= pe; 2037 op= newobject(O_SHEAD, OF_MOD, r, 30, 3); op->entry= pe; 2038 op= newobject(O_SSEC, OF_MOD, r, 34, 2); op->entry= pe; 2039 op= newobject(O_LCYL, OF_MOD, r, 40, 5); op->entry= pe; 2040 op= newobject(O_LHEAD, OF_MOD, r, 45, 3); op->entry= pe; 2041 op= newobject(O_LSEC, OF_MOD, r, 49, 2); op->entry= pe; 2042 op= newobject(O_BASE, OF_MOD, r, 59, 9); op->entry= pe; 2043 op= newobject(O_SIZE, OF_MOD, r, 69, 9); op->entry= pe; 2044 op= newobject(O_KB, OF_MOD, r, 79, 9); op->entry= pe; 2045 } 2046 2047 for (i= 1; i < argc; i++) newdevice(argv[i], 0); 2048 2049 if (firstdev == nil) { 2050 getdevices(); 2051 key= ctrl('L'); 2052 } else { 2053 key= 'r'; 2054 } 2055 2056 if (firstdev != nil) { 2057 init_tty(); 2058 clear_screen(); 2059 event(key, curobj); 2060 display(); 2061 mainloop(); 2062 reset_tty(); 2063 } 2064 exit(0); 2065 } 2066