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