Remove building with NOCRYPTO option
[minix.git] / minix / commands / autopart / autopart.c
blobd94eab4639c3e76c4622b9a0f414637c29de43cf
1 /* part 1.57 - Partition table editor Author: Kees J. Bot
2 * 13 Mar 1992
3 * Needs about 22k heap+stack.
5 * Forked july 2005 into autopart (Ben Gras), a mode which gives the user
6 * an easier time.
8 */
9 #define nil 0
10 #include <sys/types.h>
11 #include <stdio.h>
12 #include <termcap.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <stddef.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #include <dirent.h>
23 #include <limits.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <sys/ioctl.h>
27 #include <minix/config.h>
28 #include <minix/const.h>
29 #include <minix/partition.h>
30 #include <minix/com.h>
31 #include <machine/partition.h>
32 #include <termios.h>
33 #include <stdarg.h>
35 /* Declare prototype. */
36 void printstep(int step, char *message);
38 /* True if a partition is an extended partition. */
39 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
41 /* Minix master bootstrap code. */
42 char MASTERBOOT[] = "/usr/mdec/mbr";
44 /* Template:
45 ----first---- --geom/last-- ------sectors-----
46 Device Cyl Head Sec Cyl Head Sec Base Size Kb
47 /dev/c0d0 977 5 17
48 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
49 Num Sort Type
50 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440
51 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142
52 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939
53 3 p3 00 None 0 0 0 0 0 -1 0 0 0
56 #define MAXSIZE 999999999L
57 #define SECTOR_SIZE 512
58 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */
59 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */
61 int min_region_mb = 500;
63 #define MIN_REGION_SECTORS (1024*1024*min_region_mb/SECTOR_SIZE)
65 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
66 #define arraylimit(a) ((a) + arraysize(a))
68 #define SORNOT(n) ((n) == 1 ? "" : "s")
70 /* screen colours */
71 #define COL_RED 1
72 #define COL_GREEN 2
73 #define COL_ORANGE 3
74 #define COL_BLUE 4
75 #define COL_MAGENTA 5
76 #define COL_CYAN 6
78 #define SURE_SERIOUS 1
79 #define SURE_BACK 2
81 void col(int col)
83 if(!col) printf("\033[0m");
84 else printf("\033[3%dm", col % 10);
87 void type2col(int type)
89 switch(type) {
90 /* minix */
91 case 0x80:
92 case MINIX_PART: col(COL_GREEN); break;
94 /* dos/windows */
95 case 0x0B: case 0x0C: case 0x0E: case 0x0F: case 0x42:
96 case 0x07: col(COL_CYAN); break;
98 /* linux */
99 case 0x82: case 0x83: col(COL_ORANGE); break;
103 int open_ct_ok(int fd)
105 int c = -1;
106 if(ioctl(fd, DIOCOPENCT, &c) < 0) {
107 printf("Warning: couldn't verify opencount, continuing\n");
108 return 1;
111 if(c == 1) return 1;
112 if(c < 1) { printf("Error: open count %d\n", c); }
114 return 0;
117 void report(const char *label)
119 fprintf(stderr, "part: %s: %s\n", label, strerror(errno));
122 void fatal(const char *label)
124 report(label);
125 exit(1);
128 struct termios termios;
130 void restore_ttyflags(void)
131 /* Reset the tty flags to how we got 'em. */
133 if (tcsetattr(0, TCSANOW, &termios) < 0) fatal("");
136 void tty_raw(void)
137 /* Set the terminal to raw mode, no signals, no echoing. */
139 struct termios rawterm;
141 rawterm= termios;
142 rawterm.c_lflag &= ~(ICANON|ISIG|ECHO);
143 rawterm.c_iflag &= ~(ICRNL);
144 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
147 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37'))
149 char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16];
150 #define STATUSROW 10
152 int putchr(int c)
154 return putchar(c);
157 void putstr(char *s)
159 int c;
161 while ((c= *s++) != 0) putchr(c);
164 void set_cursor(int row, int col)
166 tputs(tgoto(t_cm, col, row), 1, putchr);
169 int statusrow= STATUSROW;
170 int stat_ktl= 1;
171 int need_help= 1;
173 void stat_start(int serious)
174 /* Prepare for printing on a fresh status line, possibly highlighted. */
176 set_cursor(statusrow++, 0);
177 tputs(t_cd, 1, putchr);
178 if (serious) tputs(t_so, 1, putchr);
181 void stat_end(int ktl)
182 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */
184 tputs(t_se, 1, putchr);
185 stat_ktl= ktl;
186 need_help= 1;
189 void stat_reset(void)
190 /* Reset the statusline pointer and clear old messages if expired. */
192 if (stat_ktl > 0 && --stat_ktl == 0) {
193 statusrow= STATUSROW;
194 need_help= 1;
196 if (need_help && statusrow < (24-2)) {
197 if (statusrow > STATUSROW) stat_start(0);
198 stat_start(0);
199 putstr(
200 "Type '+' or '-' to change, 'r' to read, '?' for more help, '!' for advice");
202 statusrow= STATUSROW;
203 need_help= 0;
206 void clear_screen(void)
208 set_cursor(0, 0);
209 tputs(t_cd, 1, putchr);
210 stat_ktl= 1;
211 stat_reset();
214 void reset_tty(void)
215 /* Reset the tty to cooked mode. */
217 restore_ttyflags();
218 set_cursor(statusrow, 0);
219 tputs(t_cd, 1, putchr);
222 void *alloc(size_t n)
224 void *m;
226 if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
228 return m;
231 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
233 typedef struct device {
234 struct device *next, *prev; /* Circular dequeue. */
235 dev_t rdev; /* Device number (sorting only). */
236 char *name; /* E.g. /dev/c0d0 */
237 char *subname; /* E.g. /dev/c0d0:2 */
238 parttype_t parttype;
239 int biosdrive;
240 } device_t;
242 typedef struct region {
243 /* A region is either an existing top-level partition
244 * entry (used_part is non-NULL) or free space (free_*
245 * contains data).
247 struct part_entry used_part;
248 int is_used_part;
249 int tableno;
250 int free_sec_start, free_sec_last;
251 } region_t;
253 /* A disk has between 1 and 2*partitions+1 regions;
254 * the last case is free space before and after every partition.
256 #define NR_REGIONS (2*NR_PARTITIONS+1)
257 region_t regions[NR_REGIONS];
258 int nr_partitions = 0, nr_regions = 0, free_regions, used_regions;
259 int nordonly = 0;
261 device_t *firstdev= nil, *curdev;
263 #define MAX_DEVICES 100
264 static struct {
265 device_t *dev;
266 int nr_partitions, free_regions, used_regions, sectors, nr_regions;
267 int biosdrive;
268 region_t regions[NR_REGIONS];
269 } devices[MAX_DEVICES];
271 void newdevice(char *name, int scanning, int disk_only)
272 /* Add a device to the device list. If scanning is set then we are reading
273 * /dev, so insert the device in device number order and make /dev/c0d0 current.
276 device_t *new, *nextdev, *prevdev;
277 struct stat st;
279 st.st_rdev= 0;
280 if (scanning) {
281 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
283 switch (major(st.st_rdev)) {
284 case 3:
285 /* Disk controller */
286 if (minor(st.st_rdev) >= 0x80
287 || minor(st.st_rdev) % 5 != 0) return;
288 break;
289 default:
290 return;
292 /* Interesting device found. */
293 } else {
294 if(stat(name, &st) < 0) { perror(name); return; }
297 new= alloc(sizeof(*new));
298 new->rdev= st.st_rdev;
299 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
300 strcpy(new->name, name);
301 new->subname= new->name;
302 new->parttype= DUNNO;
303 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
304 new->parttype= FLOPPY;
305 } else
306 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
307 && minor(st.st_rdev) % 5 == 0) {
308 new->parttype= PRIMARY;
311 if (firstdev == nil) {
312 firstdev= new;
313 new->next= new->prev= new;
314 curdev= firstdev;
315 return;
317 nextdev= firstdev;
318 while (new->rdev >= nextdev->rdev
319 && (nextdev= nextdev->next) != firstdev) {}
320 prevdev= nextdev->prev;
321 new->next= nextdev;
322 nextdev->prev= new;
323 new->prev= prevdev;
324 prevdev->next= new;
326 if (new->rdev < firstdev->rdev) firstdev= new;
327 if (new->rdev == DEV_C0D0) curdev= new;
328 if (curdev->rdev != DEV_C0D0) curdev= firstdev;
331 void getdevices(void)
332 /* Get all block devices from /dev that look interesting. */
334 DIR *d;
335 struct dirent *e;
336 char name[5 + NAME_MAX + 1];
338 if ((d= opendir("/dev")) == nil) fatal("/dev");
340 while ((e= readdir(d)) != nil) {
341 strcpy(name, "/dev/");
342 strcpy(name + 5, e->d_name);
343 newdevice(name, 1, 1);
345 (void) closedir(d);
348 int dirty= 0;
349 unsigned char bootblock[SECTOR_SIZE];
350 struct part_entry table[1 + NR_PARTITIONS];
351 int existing[1 + NR_PARTITIONS];
352 unsigned long offset= 0, extbase= 0, extsize;
353 int submerged= 0;
354 int sort_index[1 + NR_PARTITIONS], sort_order[1 + NR_PARTITIONS];
355 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
356 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
357 int precise= 0;
358 int device= -1;
360 unsigned long sortbase(struct part_entry *pe)
362 return pe->sysind == NO_PART ? -1 : pe->lowsec;
365 void sort(void)
366 /* Let the sort_index array show the order partitions are sorted in. */
368 int i, j;
370 for (i= 1; i <= NR_PARTITIONS; i++) sort_order[i]= i;
372 for (i= 1; i <= NR_PARTITIONS; i++) {
373 for (j= 1; j <= NR_PARTITIONS-1; j++) {
374 int sj= sort_order[j], sj1= sort_order[j+1];
376 if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
377 sort_order[j]= sj1;
378 sort_order[j+1]= sj;
382 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[sort_order[i]]= i;
385 void dos2chs(unsigned char *dos, unsigned *chs)
386 /* Extract cylinder, head and sector from the three bytes DOS uses to address
387 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
388 * of the sector byte. The sector number is rebased to count from 0.
391 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
392 chs[1]= dos[0];
393 chs[2]= (dos[1] & 0x3F) - 1;
396 void abs2dos(unsigned char *dos, unsigned long pos)
397 /* Translate a sector offset to three DOS bytes. */
399 unsigned h, c, s;
401 c= pos / secpcyl;
402 h= (pos % secpcyl) / sectors;
403 s= pos % sectors + 1;
405 dos[0]= h;
406 dos[1]= s | ((c >> 2) & 0xC0);
407 dos[2]= c & 0xFF;
410 void recompute0(void)
411 /* Recompute the partition size for the device after a geometry change. */
413 if (device < 0) {
414 cylinders= heads= sectors= 1;
415 memset(table, 0, sizeof(table));
416 } else
417 if (!precise && offset == 0) {
418 table[0].lowsec= 0;
419 table[0].size= (unsigned long) cylinders * heads * sectors;
421 table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
422 secpcyl= heads * sectors;
425 void guess_geometry(void)
426 /* With a bit of work one can deduce the disk geometry from the partition
427 * table. This may be necessary if the driver gets it wrong. (If partition
428 * tables didn't have C/H/S numbers we would not care at all...)
431 int i, n;
432 struct part_entry *pe;
433 unsigned chs[3];
434 unsigned long sec;
435 unsigned h, s;
436 unsigned char HS[256][8]; /* Bit map off all possible H/S */
438 alt_cyls= alt_heads= alt_secs= 0;
440 /* Initially all possible H/S combinations are possible. HS[h][0]
441 * bit 0 is used to rule out a head value.
443 for (h= 1; h <= 255; h++) {
444 for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
447 for (i= 0; i < 2*NR_PARTITIONS; i++) {
448 pe= &(table+1)[i >> 1];
449 if (pe->sysind == NO_PART) continue;
451 /* Get the end or start sector numbers (in that order). */
452 if ((i & 1) == 0) {
453 dos2chs(&pe->last_head, chs);
454 sec= pe->lowsec + pe->size - 1;
455 } else {
456 dos2chs(&pe->start_head, chs);
457 sec= pe->lowsec;
460 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
462 /* Which H/S combinations can be ruled out? */
463 for (h= 1; h <= 255; h++) {
464 if (HS[h][0] == 0) continue;
465 n = 0;
466 for (s= 1; s <= 63; s++) {
467 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
468 HS[h][s/8] &= ~(1 << (s%8));
470 if (HS[h][s/8] & (1 << (s%8))) n++;
472 if (n == 0) HS[h][0]= 0;
476 /* See if only one remains. */
477 i= 0;
478 for (h= 1; h <= 255; h++) {
479 if (HS[h][0] == 0) continue;
480 for (s= 1; s <= 63; s++) {
481 if (HS[h][s/8] & (1 << (s%8))) {
482 i++;
483 alt_heads= h;
484 alt_secs= s;
489 /* Forget it if more than one choice... */
490 if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
493 void geometry(void)
494 /* Find out the geometry of the device by querying the driver, or by looking
495 * at the partition table. These numbers are crosschecked to make sure that
496 * the geometry is correct. Master bootstraps other than the Minix one use
497 * the CHS numbers in the partition table to load the bootstrap of the active
498 * partition.
501 struct stat dst;
502 int err= 0;
503 struct part_geom geometry;
505 if (submerged) {
506 /* Geometry already known. */
507 sort();
508 return;
510 precise= 0;
511 cylinders= 0;
512 recompute0();
513 if (device < 0) return;
515 /* Try to guess the geometry from the partition table. */
516 guess_geometry();
518 /* Try to get the geometry from the driver. */
519 (void) fstat(device, &dst);
521 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
522 /* Try to get the drive's geometry from the driver. */
524 if (ioctl(device, DIOCGETP, &geometry) < 0)
525 err= errno;
526 else {
527 table[0].lowsec= (unsigned long)(geometry.base /
528 SECTOR_SIZE);
529 table[0].size = (unsigned long)(geometry.size /
530 SECTOR_SIZE);
531 cylinders= geometry.cylinders;
532 heads= geometry.heads;
533 sectors= geometry.sectors;
534 precise= 1;
536 } else {
537 err= ENODEV;
540 if (err != 0) {
541 /* Getting the geometry from the driver failed, so use the
542 * alternate geometry.
544 if (alt_heads == 0) {
545 alt_cyls= table[0].size / (64 * 32);
546 alt_heads= 64;
547 alt_secs= 32;
550 cylinders= alt_cyls;
551 heads= alt_heads;
552 sectors= alt_secs;
554 stat_start(1);
555 printf("Failure to get the geometry of %s: %s", curdev->name,
556 errno == ENOTTY ? "No driver support" : strerror(err));
557 stat_end(5);
558 stat_start(0);
559 printf("The geometry has been guessed as %ux%ux%u",
560 cylinders, heads, sectors);
561 stat_end(5);
562 } else {
563 if (alt_heads == 0) {
564 alt_cyls= cylinders;
565 alt_heads= heads;
566 alt_secs= sectors;
569 if (heads != alt_heads || sectors != alt_secs) {
570 printf(
571 "The geometry obtained from the driver\n"
572 "does not match the geometry implied by the partition\n"
573 "table. Please use expert mode instead.\n");
574 exit(1);
578 /* Show the base and size of the device instead of the whole drive.
579 * This makes sense for subpartitioning primary partitions.
581 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
582 table[0].lowsec= (unsigned long)(geometry.base / SECTOR_SIZE);
583 table[0].size = (unsigned long)(geometry.size / SECTOR_SIZE);
584 } else {
585 precise= 0;
587 recompute0();
588 sort();
591 typedef struct indicators { /* Partition type to partition name. */
592 unsigned char ind;
593 char name[10];
594 } indicators_t;
596 indicators_t ind_table[]= {
597 { 0x00, "None" },
598 { 0x01, "FAT-12" },
599 { 0x02, "XENIX /" },
600 { 0x03, "XENIX usr" },
601 { 0x04, "FAT-16" },
602 { 0x05, "EXTENDED" },
603 { 0x06, "FAT-16" },
604 { 0x07, "HPFS/NTFS" },
605 { 0x08, "AIX" },
606 { 0x09, "COHERENT" },
607 { 0x0A, "OS/2" },
608 { 0x0B, "FAT-32" },
609 { 0x0C, "FAT?" },
610 { 0x0E, "FAT?" },
611 { 0x0F, "EXTENDED" },
612 { 0x10, "OPUS" },
613 { 0x40, "VENIX286" },
614 { 0x42, "W2000 Dyn" },
615 { 0x52, "MICROPORT" },
616 { 0x63, "386/IX" },
617 { 0x64, "NOVELL286" },
618 { 0x65, "NOVELL386" },
619 { 0x75, "PC/IX" },
620 { 0x80, "MINIX-OLD" },
621 { 0x81, "MINIX" },
622 { 0x82, "LINUXswap" },
623 { 0x83, "LINUX" },
624 { 0x93, "AMOEBA" },
625 { 0x94, "AMOEBAbad" },
626 { 0xA5, "386BSD" },
627 { 0xB7, "BSDI" },
628 { 0xB8, "BSDI swap" },
629 { 0xC7, "SYRINX" },
630 { 0xDB, "CPM" },
631 { 0xFF, "BADBLOCKS" },
634 char *typ2txt(int ind)
635 /* Translate a numeric partition indicator for human eyes. */
637 indicators_t *pind;
639 for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
640 if (pind->ind == ind) return pind->name;
642 return "unknown system";
645 int round_sysind(int ind, int delta)
646 /* Find the next known partition type starting with ind in direction delta. */
648 indicators_t *pind;
650 ind= (ind + delta) & 0xFF;
652 if (delta < 0) {
653 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
654 } else {
655 for (pind= ind_table; pind->ind < ind; pind++) {}
657 return pind->ind;
660 /* Objects on the screen, either simple pieces of the text or the cylinder
661 * number of the start of partition three.
663 typedef enum objtype {
664 O_INFO, O_TEXT, O_DEV, O_SUB,
665 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
666 O_CYL, O_HEAD, O_SEC,
667 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
668 } objtype_t;
670 #define rjust(type) ((type) >= O_TYPHEX)
671 #define computed(type) ((type) >= O_TYPTXT)
673 typedef struct object {
674 struct object *next;
675 objtype_t type; /* Text field, cylinder number, etc. */
676 char flags; /* Modifiable? */
677 char row;
678 char col;
679 char len;
680 struct part_entry *entry; /* What does the object refer to? */
681 char *text;
682 char value[20]; /* Value when printed. */
683 } object_t;
685 #define OF_MOD 0x01 /* Object value is modifiable. */
686 #define OF_ODD 0x02 /* It has a somewhat odd value. */
687 #define OF_BAD 0x04 /* Its value is no good at all. */
689 /* Events: (Keypress events are the value of the key pressed.) */
690 #define E_ENTER (-1) /* Cursor moves onto object. */
691 #define E_LEAVE (-2) /* Cursor leaves object. */
692 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
694 /* The O_SIZE objects have a dual identity. */
695 enum howend { SIZE, LAST } howend= SIZE;
697 object_t *world= nil;
698 object_t *curobj= nil;
700 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
701 /* Make a new object given a type, flags, position and length on the screen. */
703 object_t *new;
704 object_t **aop= &world;
706 new= alloc(sizeof(*new));
708 new->type= type;
709 new->flags= flags;
710 new->row= row;
711 new->col= col;
712 new->len= len;
713 new->entry= nil;
714 new->text= "";
715 new->value[0]= 0;
717 new->next= *aop;
718 *aop= new;
720 return new;
723 unsigned long entry2base(struct part_entry *pe)
724 /* Return the base sector of the partition if defined. */
726 return pe->sysind == NO_PART ? 0 : pe->lowsec;
729 unsigned long entry2last(struct part_entry *pe)
731 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
734 unsigned long entry2size(struct part_entry *pe)
736 return pe->sysind == NO_PART ? 0 : pe->size;
739 int typing; /* Set if a digit has been typed to set a value. */
740 int magic; /* Changes when using the magic key. */
742 void event(int ev, object_t *op);
744 void m_redraw(int ev, object_t *op)
745 /* Redraw the screen. */
747 object_t *op2;
749 if (ev != ctrl('L')) return;
751 clear_screen();
752 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
755 void m_toggle(int ev, object_t *op)
756 /* Toggle between the driver and alternate geometry. */
758 unsigned t;
760 if (ev != 'X') return;
761 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
762 return;
764 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
765 t= heads; heads= alt_heads; alt_heads= t;
766 t= sectors; sectors= alt_secs; alt_secs= t;
767 dirty= 1;
768 recompute0();
771 char size_last[]= "Size";
773 void m_orientation(int ev, object_t *op)
775 if (ev != ' ') return;
777 switch (howend) {
778 case SIZE:
779 howend= LAST;
780 strcpy(size_last, "Last");
781 break;
782 case LAST:
783 howend= SIZE;
784 strcpy(size_last, "Size");
788 void m_move(int ev, object_t *op)
789 /* Move to the nearest modifiably object in the intended direction. Objects
790 * on the same row or column are really near.
793 object_t *near, *op2;
794 unsigned dist, d2, dr, dc;
796 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
797 return;
799 if (device < 0) {
800 /* No device open? Then try to read first. */
801 event('r', op);
802 if (device < 0) return;
805 near= op;
806 dist= -1;
808 for (op2= world; op2 != nil; op2= op2->next) {
809 if (op2 == op || !(op2->flags & OF_MOD)) continue;
811 dr= abs(op2->row - op->row);
812 dc= abs(op2->col - op->col);
814 d2= 25*dr*dr + dc*dc;
815 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
817 switch (ev) {
818 case 'h': /* Left */
819 if (op2->col >= op->col) d2= -1;
820 break;
821 case 'j': /* Down */
822 if (op2->row <= op->row) d2= -1;
823 break;
824 case 'k': /* Up */
825 if (op2->row >= op->row) d2= -1;
826 break;
827 case 'l': /* Right */
828 if (op2->col <= op->col) d2= -1;
829 break;
830 case 'H': /* Home */
831 if (op2->type == O_DEV) d2= 0;
833 if (d2 < dist) { near= op2; dist= d2; }
835 if (near != op) event(E_LEAVE, op);
836 event(E_ENTER, near);
839 void m_updown(int ev, object_t *op)
840 /* Move a partition table entry up or down. */
842 int i, j;
843 struct part_entry tmp;
844 int tmpx;
846 if (ev != ctrl('K') && ev != ctrl('J')) return;
847 if (op->entry == nil) return;
849 i= op->entry - table;
850 if (ev == ctrl('K')) {
851 if (i <= 1) return;
852 j= i-1;
853 } else {
854 if (i >= NR_PARTITIONS) return;
855 j= i+1;
858 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
859 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
860 sort();
861 dirty= 1;
862 event(ev == ctrl('K') ? 'k' : 'j', op);
865 void m_enter(int ev, object_t *op)
866 /* We've moved onto this object. */
868 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
869 return;
870 curobj= op;
871 typing= 0;
872 magic= 0;
875 void m_leave(int ev, object_t *op)
876 /* About to leave this object. */
878 if (ev != E_LEAVE) return;
881 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
882 /* Only set *var to value if it looks reasonable. */
884 if (low <= value && value <= high) {
885 *var= value;
886 return 1;
887 } else
888 return 0;
891 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
892 unsigned long high)
894 if (low <= value && value <= high) {
895 *var= value;
896 return 1;
897 } else
898 return 0;
901 int nextdevice(object_t *op, int delta)
902 /* Select the next or previous device from the device list. */
904 dev_t rdev;
906 if (offset != 0) return 0;
907 if (dirty) event(E_WRITE, op);
908 if (dirty) return 0;
910 if (device >= 0) {
911 (void) close(device);
912 device= -1;
914 recompute0();
916 rdev= curdev->rdev;
917 if (delta < 0) {
919 curdev= curdev->prev;
920 while (delta < -1 && major(curdev->rdev) == major(rdev)
921 && curdev->rdev < rdev);
922 } else {
924 curdev= curdev->next;
925 while (delta > 1 && major(curdev->rdev) == major(rdev)
926 && curdev->rdev > rdev);
928 return 1;
931 void check_ind(struct part_entry *pe)
932 /* If there are no other partitions then make this new one active. */
934 struct part_entry *pe2;
935 int i = 0;
937 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++, i++)
938 if (pe2->sysind != NO_PART && (pe2->bootind & ACTIVE_FLAG))
939 return;
941 pe->bootind= ACTIVE_FLAG;
942 dirty = 1;
945 int check_existing(struct part_entry *pe)
946 /* Check and if not ask if an existing partition may be modified. */
948 static int expert= 0;
949 int c;
951 if (expert || pe == nil || !existing[pe - table]) return 1;
953 stat_start(1);
954 putstr("Do you wish to modify existing partitions? (y/n) ");
955 fflush(stdout);
956 while ((c= getchar()) != 'y' && c != 'n') {}
957 putchr(c);
958 stat_end(3);
959 return (expert= (c == 'y'));
962 void m_modify(int ev, object_t *op)
963 /* Increment, decrement, set, or toggle the value of an object, using
964 * arithmetic tricks the author doesn't understand either.
967 object_t *op2;
968 struct part_entry *pe= op->entry;
969 int mul, delta;
970 unsigned level= 1;
971 unsigned long surplus;
972 int radix= op->type == O_TYPHEX ? 0x10 : 10;
973 unsigned long t;
975 if (device < 0 && op->type != O_DEV) return;
977 switch (ev) {
978 case '-':
979 mul= radix; delta= -1; typing= 0;
980 break;
981 case '+':
982 mul= radix; delta= 1; typing= 0;
983 break;
984 case '\b':
985 if (!typing) return;
986 mul= 1; delta= 0;
987 break;
988 case '\r':
989 typing= 0;
990 return;
991 default:
992 if ('0' <= ev && ev <= '9')
993 delta= ev - '0';
994 else
995 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
996 delta= ev - 'a' + 10;
997 else
998 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
999 delta= ev - 'A' + 10;
1000 else
1001 return;
1003 mul= typing ? radix*radix : 0;
1004 typing= 1;
1006 magic= 0;
1008 if (!check_existing(pe)) return;
1010 switch (op->type) {
1011 case O_DEV:
1012 if (ev != '-' && ev != '+') return;
1013 if (!nextdevice(op, delta)) return;
1014 break;
1015 case O_CYL:
1016 if (!within(&cylinders, 1,
1017 cylinders * mul / radix + delta, 1024)) return;
1018 recompute0();
1019 break;
1020 case O_HEAD:
1021 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1022 return;
1023 recompute0();
1024 break;
1025 case O_SEC:
1026 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1027 return;
1028 recompute0();
1029 break;
1030 case O_NUM:
1031 if (ev != '-' && ev != '+') return;
1032 for (op2= world; op2 != nil; op2= op2->next) {
1033 if (op2->type == O_NUM && ev == '+')
1034 op2->entry->bootind= 0;
1036 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1037 break;
1038 case O_TYPHEX:
1039 check_ind(pe);
1040 pe->sysind= pe->sysind * mul / radix + delta;
1041 break;
1042 case O_TYPTXT:
1043 if (ev != '-' && ev != '+') return;
1044 check_ind(pe);
1045 pe->sysind= round_sysind(pe->sysind, delta);
1046 break;
1047 case O_SCYL:
1048 level= heads;
1049 case O_SHEAD:
1050 level*= sectors;
1051 case O_SSEC:
1052 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1053 case O_BASE:
1054 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1055 t= pe->lowsec;
1056 surplus= t % level;
1057 if (!lwithin(&t, 0L,
1058 (t / level * mul / radix + delta) * level + surplus,
1059 MAXSIZE)) return;
1060 if (howend == LAST || op->type != O_BASE)
1061 pe->size-= t - pe->lowsec;
1062 pe->lowsec= t;
1063 check_ind(pe);
1064 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1065 break;
1066 case O_LCYL:
1067 level= heads;
1068 case O_LHEAD:
1069 level*= sectors;
1070 case O_LSEC:
1071 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1073 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1074 t= pe->lowsec + pe->size - 1 + level;
1075 surplus= t % level - mul / radix * level;
1076 if (!lwithin(&t, 0L,
1077 (t / level * mul / radix + delta) * level + surplus,
1078 MAXSIZE)) return;
1079 pe->size= t - pe->lowsec + 1;
1080 check_ind(pe);
1081 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1082 break;
1083 case O_KB:
1084 level= 2;
1085 if (mul == 0) pe->size= 0; /* new value, no surplus */
1086 case O_SIZE:
1087 if (pe->sysind == NO_PART) {
1088 if (op->type == O_KB || howend == SIZE) {
1089 /* First let loose magic to set the base. */
1090 event('m', op);
1091 magic= 0;
1092 pe->size= 0;
1093 event(ev, op);
1094 return;
1096 memset(pe, 0, sizeof(*pe));
1098 t= (op->type == O_KB || howend == SIZE) ? pe->size
1099 : pe->lowsec + pe->size - 1;
1100 surplus= t % level;
1101 if (!lwithin(&t, 0L,
1102 (t / level * mul / radix + delta) * level + surplus,
1103 MAXSIZE)) return;
1104 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1105 t - pe->lowsec + 1;
1106 check_ind(pe);
1107 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1108 break;
1109 default:
1110 return;
1113 /* The order among the entries may have changed. */
1114 sort();
1115 dirty= 1;
1118 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1119 int nspells;
1120 objtype_t touching;
1122 void newspell(unsigned long charm)
1123 /* Add a new spell, descending order for the base, ascending for the size. */
1125 int i, j;
1127 if (charm - table[0].lowsec > table[0].size) return;
1129 for (i= 0; i < nspells; i++) {
1130 if (charm == spell[i]) return; /* duplicate */
1132 if (touching == O_BASE) {
1133 if (charm == table[0].lowsec + table[0].size) return;
1134 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1135 } else {
1136 if (charm == table[0].lowsec) return;
1137 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1140 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1141 spell[i]= charm;
1144 void m_magic(int ev, object_t *op)
1145 /* Apply magic onto a base or size number. */
1147 struct part_entry *pe= op->entry, *pe2;
1148 int rough= (offset != 0 && extbase == 0);
1150 if (ev != 'm' || device < 0) return;
1151 typing= 0;
1153 if (!check_existing(pe)) return;
1155 if (magic == 0) {
1156 /* See what magic we can let loose on this value. */
1157 nspells= 1;
1159 /* First spell, the current value. */
1160 switch (op->type) {
1161 case O_SCYL:
1162 case O_SHEAD: /* Start of partition. */
1163 case O_SSEC:
1164 case O_BASE:
1165 touching= O_BASE;
1166 spell[0]= pe->lowsec;
1167 break;
1168 case O_LCYL:
1169 case O_LHEAD:
1170 case O_LSEC: /* End of partition. */
1171 case O_KB:
1172 case O_SIZE:
1173 touching= O_SIZE;
1174 spell[0]= pe->lowsec + pe->size;
1175 break;
1176 default:
1177 return;
1179 if (pe->sysind == NO_PART) {
1180 memset(pe, 0, sizeof(*pe));
1181 check_ind(pe);
1182 pe->sysind= MINIX_PART;
1183 spell[0]= 0;
1184 if (touching == O_SIZE) {
1185 /* First let loose magic on the base. */
1186 object_t *op2;
1188 for (op2= world; op2 != nil; op2= op2->next) {
1189 if (op2->row == op->row &&
1190 op2->type == O_BASE) {
1191 event('m', op2);
1194 magic= 0;
1195 event('m', op);
1196 return;
1199 /* Avoid the first sector on the device. */
1200 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1202 /* Further interesting values are the the bases of other
1203 * partitions or their ends.
1205 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1206 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1207 if (pe2->lowsec == table[0].lowsec)
1208 newspell(table[0].lowsec + 1);
1209 else
1210 newspell(pe2->lowsec);
1211 newspell(pe2->lowsec + pe2->size);
1212 if (touching == O_BASE && howend == SIZE) {
1213 newspell(pe2->lowsec - pe->size);
1214 newspell(pe2->lowsec + pe2->size - pe->size);
1216 if (pe2->lowsec % sectors != 0) rough= 1;
1218 /* Present values rounded up to the next cylinder unless
1219 * the table is already a mess. Use "start + 1 track" instead
1220 * of "start + 1 cylinder". Also add the end of the last
1221 * cylinder.
1223 if (!rough) {
1224 unsigned long n= spell[0];
1225 if (n == table[0].lowsec) n++;
1226 n= (n + sectors - 1) / sectors * sectors;
1227 if (n != table[0].lowsec + sectors)
1228 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1229 newspell(n);
1230 if (touching == O_SIZE)
1231 newspell(table[0].size / secpcyl * secpcyl);
1234 /* Magic has been applied, a spell needs to be chosen. */
1236 if (++magic == nspells) magic= 0;
1238 if (touching == O_BASE) {
1239 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1240 pe->lowsec= spell[magic];
1241 } else
1242 pe->size= spell[magic] - pe->lowsec;
1244 /* The order among the entries may have changed. */
1245 sort();
1246 dirty= 1;
1249 typedef struct diving {
1250 struct diving *up;
1251 struct part_entry old0;
1252 char *oldsubname;
1253 parttype_t oldparttype;
1254 unsigned long oldoffset;
1255 unsigned long oldextbase;
1256 } diving_t;
1258 diving_t *diving= nil;
1260 void m_in(int ev, object_t *op)
1261 /* Go down into a primary or extended partition. */
1263 diving_t *newdiv;
1264 struct part_entry *pe= op->entry, ext;
1265 int n;
1267 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1268 || (!(pe->sysind == MINIX_PART && offset == 0)
1269 && !ext_part(pe->sysind))
1270 || pe->size == 0) return;
1272 ext= *pe;
1273 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1275 if (dirty) event(E_WRITE, op);
1276 if (dirty) return;
1277 if (device >= 0) { close(device); device= -1; }
1279 newdiv= alloc(sizeof(*newdiv));
1280 newdiv->old0= table[0];
1281 newdiv->oldsubname= curdev->subname;
1282 newdiv->oldparttype= curdev->parttype;
1283 newdiv->oldoffset= offset;
1284 newdiv->oldextbase= extbase;
1285 newdiv->up= diving;
1286 diving= newdiv;
1288 table[0]= ext;
1290 n= strlen(diving->oldsubname);
1291 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1292 strcpy(curdev->subname, diving->oldsubname);
1293 curdev->subname[n++]= ':';
1294 curdev->subname[n++]= '0' + (pe - table - 1);
1295 curdev->subname[n]= 0;
1297 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1298 offset= ext.lowsec;
1299 if (ext_part(ext.sysind) && extbase == 0) {
1300 extbase= ext.lowsec;
1301 extsize= ext.size;
1302 curdev->parttype= DUNNO;
1305 submerged= 1;
1306 event('r', op);
1309 void m_out(int ev, object_t *op)
1310 /* Go up from an extended or subpartition table to its enclosing. */
1312 diving_t *olddiv;
1314 if (ev != '<' || diving == nil) return;
1316 if (dirty) event(E_WRITE, op);
1317 if (dirty) return;
1318 if (device >= 0) { close(device); device= -1; }
1320 olddiv= diving;
1321 diving= olddiv->up;
1323 table[0]= olddiv->old0;
1325 free(curdev->subname);
1326 curdev->subname= olddiv->oldsubname;
1328 curdev->parttype= olddiv->oldparttype;
1329 offset= olddiv->oldoffset;
1330 extbase= olddiv->oldextbase;
1332 free(olddiv);
1334 event('r', op);
1335 if (diving == nil) submerged= 0; /* We surfaced. */
1338 void installboot(unsigned char *bootblock, char *masterboot)
1339 /* Install code from a master bootstrap into a boot block. */
1341 FILE *mfp;
1342 unsigned char buf[SECTOR_SIZE];
1343 int n;
1344 char *err;
1346 if ((mfp= fopen(masterboot, "r")) == nil) {
1347 err= strerror(errno);
1348 goto m_err;
1351 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1352 if (ferror(mfp)) {
1353 err= strerror(errno);
1354 fclose(mfp);
1355 goto m_err;
1357 else if (n < 256) {
1358 err= "Is probably not a boot sector, too small";
1359 fclose(mfp);
1360 goto m_err;
1362 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1363 /* if only code, it cannot override partition table */
1364 err= "Does not fit in a boot sector";
1365 fclose(mfp);
1366 goto m_err;
1368 else if (n == SECTOR_SIZE) {
1369 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1370 err= "Is not a boot sector (bad magic)";
1371 fclose(mfp);
1372 goto m_err;
1374 n = PART_TABLE_OFF;
1377 if (n > PART_TABLE_OFF) {
1378 err= "Does not fit in a boot sector";
1379 fclose(mfp);
1380 goto m_err;
1383 memcpy(bootblock, buf, n);
1384 fclose(mfp);
1386 /* Bootstrap installed. */
1387 return;
1389 m_err:
1390 stat_start(1);
1391 printf("%s: %s", masterboot, err);
1392 stat_end(5);
1395 ssize_t boot_readwrite(int rw)
1396 /* Read (0) or write (1) the boot sector. */
1398 int r = 0;
1400 if (lseek(device, offset * SECTOR_SIZE, SEEK_SET) < 0)
1401 return -1;
1403 switch (rw) {
1404 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1405 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1408 return r;
1411 int cylinderalign(region_t *reg)
1413 if(reg->is_used_part) {
1414 if(reg->used_part.lowsec != table[0].lowsec + sectors
1415 && (reg->used_part.lowsec % secpcyl)) {
1416 int extra;
1417 extra = secpcyl - (reg->used_part.lowsec % secpcyl);
1418 reg->used_part.lowsec += extra;
1419 reg->used_part.size -= extra;
1421 if((reg->used_part.size+1) % secpcyl) {
1422 reg->used_part.size -= secpcyl - ((reg->used_part.size + 1) % secpcyl);
1424 return reg->used_part.size > 0;
1427 if(reg->free_sec_start != table[0].lowsec + sectors && (reg->free_sec_start % secpcyl)) {
1428 /* Start is unaligned. Round up. */
1429 reg->free_sec_start += secpcyl - (reg->free_sec_start % secpcyl);
1431 if((reg->free_sec_last+1) % secpcyl) {
1432 /* End is unaligned. Round down. */
1433 reg->free_sec_last -= (reg->free_sec_last+1) % secpcyl;
1436 /* Return nonzero if anything remains of the region after rounding. */
1437 return reg->free_sec_last > reg->free_sec_start;
1440 void regionize(void)
1442 int free_sec, i, si;
1444 sort();
1446 free_sec = table[0].lowsec + sectors;
1448 /* Create region data used in autopart mode. */
1449 free_regions = used_regions = nr_regions = nr_partitions = 0;
1450 if(table[0].lowsec > table[sort_order[1]].lowsec &&
1451 table[sort_order[1]].sysind != NO_PART) {
1452 printf("\nSanity check failed on %s - first partition starts before disk.\n"
1453 "Please use expert mode to correct it.\n", curdev->name);
1454 exit(1);
1456 for(si = 1; si <= NR_PARTITIONS; si++) {
1457 i = sort_order[si];
1458 if(i < 1 || i > NR_PARTITIONS) {
1459 printf("Sorry, something unexpected has happened (%d out of range).\n", i);
1460 exit(1);
1463 if(table[i].sysind == NO_PART)
1464 break;
1466 /* Free space before this partition? */
1467 if(table[i].lowsec > free_sec) {
1468 /* Free region before this partition. */
1469 regions[nr_regions].free_sec_start = free_sec;
1470 regions[nr_regions].free_sec_last = table[i].lowsec-1;
1471 regions[nr_regions].is_used_part = 0;
1472 if(cylinderalign(&regions[nr_regions])) {
1473 nr_regions++;
1474 free_regions++;
1478 /* Sanity check. */
1479 if(si > 1) {
1480 if(table[i].lowsec < table[sort_order[si-1]].lowsec ||
1481 table[i].lowsec < table[sort_order[si-1]].lowsec + table[sort_order[si-1]].size) {
1482 printf("\nSanity check failed on %s - partitions overlap.\n"
1483 "Please use expert mode to correct it.\n", curdev->name);
1484 exit(1);
1487 if(table[i].size > table[0].size) {
1488 printf("\nSanity check failed on %s - partition is larger than disk.\n"
1489 "Please use expert mode to correct it.\n", curdev->name);
1490 exit(1);
1492 if(table[i].size < 1) {
1493 printf("\nSanity check failed on %s - zero-sized partition.\n"
1494 "Please use expert mode to correct it.\n", curdev->name);
1495 exit(1);
1498 /* Remember used region. */
1499 memcpy(&regions[nr_regions].used_part, &table[i], sizeof(table[i]));
1500 free_sec = table[i].lowsec+table[i].size;
1501 regions[nr_regions].is_used_part = 1;
1502 regions[nr_regions].tableno = i;
1503 nr_partitions++;
1504 nr_regions++;
1505 used_regions++;
1508 /* Special case: space after partitions. */
1509 if(free_sec < table[0].lowsec + table[0].size-1) {
1510 regions[nr_regions].free_sec_start = free_sec;
1511 regions[nr_regions].free_sec_last = table[0].lowsec + table[0].size-1;
1512 regions[nr_regions].is_used_part = 0;
1513 if(cylinderalign(&regions[nr_regions])) {
1514 nr_regions++;
1515 free_regions++;
1521 void m_read(int ev, int *biosdrive)
1522 /* Read the partition table from the current device. */
1524 int i, n, v;
1525 struct part_entry *pe;
1526 u32_t system_hz;
1528 if (ev != 'r' || device >= 0) return;
1530 /* Open() may cause kernel messages: */
1531 stat_start(0);
1532 fflush(stdout);
1534 device = open(curdev->name, O_RDWR, 0666);
1535 if (device < 0) {
1536 return;
1539 system_hz = (u32_t) sysconf(_SC_CLK_TCK);
1540 v = 2*system_hz;
1541 ioctl(device, DIOCTIMEOUT, &v);
1543 memset(bootblock, 0, sizeof(bootblock));
1545 n= boot_readwrite(0);
1547 if (n <= 0) stat_start(1);
1548 if (n < 0) {
1549 close(device);
1550 device= -1;
1551 } else
1552 if (n < SECTOR_SIZE) {
1553 close(device);
1554 device= -1;
1555 return;
1557 if (n <= 0) stat_end(5);
1559 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1561 if(biosdrive) (*biosdrive)++;
1563 if(!open_ct_ok(device)) {
1564 printf("\n%s: device in use! skipping it.", curdev->subname);
1565 fflush(stdout);
1566 close(device);
1567 device= -1;
1568 return;
1571 memcpy(table+1, bootblock+PART_TABLE_OFF,
1572 NR_PARTITIONS * sizeof(table[1]));
1573 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1574 /* Invalid boot block, install bootstrap, wipe partition table.
1576 memset(bootblock, 0, sizeof(bootblock));
1577 installboot(bootblock, MASTERBOOT);
1578 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1581 /* Fix an extended partition table up to something mere mortals can
1582 * understand. Record already defined partitions.
1584 for (i= 1; i <= NR_PARTITIONS; i++) {
1585 pe= &table[i];
1586 if (extbase != 0 && pe->sysind != NO_PART)
1587 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1588 existing[i]= pe->sysind != NO_PART;
1590 geometry();
1591 dirty= 0;
1593 /* Warn about grave dangers ahead. */
1594 if (extbase != 0) {
1595 stat_start(1);
1596 printf("Warning: You are in an extended partition.");
1597 stat_end(5);
1600 regionize();
1603 void m_write(int ev, object_t *op)
1604 /* Write the partition table back if modified. */
1606 struct part_entry new_table[NR_PARTITIONS], *pe;
1608 if (ev != 'w' && ev != E_WRITE) return;
1609 if (device < 0) { dirty= 0; return; }
1610 if (!dirty) {
1611 if (ev == 'w') {
1612 stat_start(1);
1613 printf("%s is not changed, or has already been written",
1614 curdev->subname);
1615 stat_end(2);
1617 return;
1620 if (extbase != 0) {
1621 /* Will this stop him? Probably not... */
1622 stat_start(1);
1623 printf("You have changed an extended partition. Bad Idea.");
1624 stat_end(5);
1627 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1628 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1629 if (pe->sysind == NO_PART) {
1630 memset(pe, 0, sizeof(*pe));
1631 } else {
1632 abs2dos(&pe->start_head, pe->lowsec);
1633 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1635 /* Fear and loathing time: */
1636 if (extbase != 0)
1637 pe->lowsec-= ext_part(pe->sysind)
1638 ? extbase : offset;
1641 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1642 bootblock[510]= 0x55;
1643 bootblock[511]= 0xAA;
1645 if (boot_readwrite(1) < 0) {
1646 stat_start(1);
1647 printf("%s: %s", curdev->name, strerror(errno));
1648 stat_end(5);
1649 return;
1651 dirty= 0;
1654 void m_shell(int ev, object_t *op)
1655 /* Shell escape, to do calculations for instance. */
1657 int r, pid, status;
1658 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1660 if (ev != 's') return;
1662 reset_tty();
1663 fflush(stdout);
1665 switch (pid= fork()) {
1666 case -1:
1667 stat_start(1);
1668 printf("can't fork: %s\n", strerror(errno));
1669 stat_end(3);
1670 break;
1671 case 0:
1672 if (device >= 0) (void) close(device);
1673 execl("/bin/sh", "sh", (char *) nil);
1674 r= errno;
1675 stat_start(1);
1676 printf("/bin/sh: %s\n", strerror(errno));
1677 stat_end(3);
1678 exit(127);
1680 sigint= signal(SIGINT, SIG_IGN);
1681 sigquit= signal(SIGQUIT, SIG_IGN);
1682 sigterm= signal(SIGTERM, SIG_IGN);
1683 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1684 (void) signal(SIGINT, sigint);
1685 (void) signal(SIGQUIT, sigquit);
1686 (void) signal(SIGTERM, sigterm);
1687 tty_raw();
1688 if (pid < 0)
1690 else
1691 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1692 stat_start(0); /* Match the stat_start in the child. */
1693 else
1694 event(ctrl('L'), op);
1697 int quitting= 0;
1699 void m_quit(int ev, object_t *op)
1700 /* Write the partition table if modified and exit. */
1702 if (ev != 'q' && ev != 'x') return;
1704 quitting= 1;
1706 if (dirty) event(E_WRITE, op);
1707 if (dirty) quitting= 0;
1710 void m_help(int ev, object_t *op)
1711 /* For people without a clue; let's hope they can find the '?' key. */
1713 static struct help {
1714 char *keys;
1715 char *what;
1716 } help[]= {
1717 { "? !", "This help / more advice!" },
1718 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1719 { "0-9 (a-f)", "Enter value" },
1720 { "hjkl (arrow keys)", "Move around" },
1721 { "CTRL-K CTRL-J", "Move entry up/down" },
1722 { "CTRL-L", "Redraw screen" },
1723 { ">", "Start a subpartition table" },
1724 { "<", "Back to the primary partition table" },
1725 { "m", "Cycle through magic values" },
1726 { "spacebar", "Show \"Size\" or \"Last\"" },
1727 { "r w", "Read/write partition table" },
1728 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1729 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1731 static char *advice[] = {
1732 "* Choose a disk with '+' and '-', then hit 'r'.",
1733 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1734 "* To make a new partition: Move over to the Size or Kb field of an unused",
1735 " partition and type the size. Hit the 'm' key to pad the partition out to",
1736 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1737 " You can hit 'm' more than once on a base or size field to see several",
1738 " interesting values go by. Note: Other Operating Systems can be picky about",
1739 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1740 " head or sector numbers.",
1741 "* To reuse a partition: Change the type to MINIX.",
1742 "* To delete a partition: Type a zero in the hex Type field.",
1743 "* To make a partition active: Type '+' in the Num field.",
1744 "* To study the list of keys: Type '?'.",
1747 if (ev == '?') {
1748 struct help *hp;
1750 for (hp= help; hp < arraylimit(help); hp++) {
1751 stat_start(0);
1752 printf("%-25s - %s", hp->keys, hp->what);
1753 stat_end(0);
1755 stat_start(0);
1756 putstr("Things like ");
1757 putstr(t_so); putstr("this"); putstr(t_se);
1758 putstr(" must be checked, but ");
1759 putstr(t_md); putstr("this"); putstr(t_me);
1760 putstr(" is not really a problem");
1761 stat_end(0);
1762 } else
1763 if (ev == '!') {
1764 char **ap;
1766 for (ap= advice; ap < arraylimit(advice); ap++) {
1767 stat_start(0);
1768 putstr(*ap);
1769 stat_end(0);
1774 void event(int ev, object_t *op)
1775 /* Simply call all modifiers for an event, each one knows when to act. */
1777 m_help(ev, op);
1778 m_redraw(ev, op);
1779 m_toggle(ev, op);
1780 m_orientation(ev, op);
1781 m_move(ev, op);
1782 m_updown(ev, op);
1783 m_enter(ev, op);
1784 m_leave(ev, op);
1785 m_modify(ev, op);
1786 m_magic(ev, op);
1787 m_in(ev, op);
1788 m_out(ev, op);
1789 m_read(ev, NULL);
1790 m_write(ev, op);
1791 m_shell(ev, op);
1792 m_quit(ev, op);
1795 char *
1796 prettysizeprint(int kb)
1798 int toosmall = 0;
1799 static char str[200];
1800 char unit = 'k';
1801 if(MIN_REGION_SECTORS > kb*2)
1802 toosmall = 1;
1803 if(kb >= 5*1024) {
1804 kb /= 1024;
1805 unit = 'M';
1806 if(kb >= 5*1024) {
1807 kb /= 1024;
1808 unit = 'G';
1811 sprintf(str, "%4d %cB%s", kb, unit,
1812 toosmall ? ", too small for MINIX 3" : "");
1813 return str;
1816 void
1817 printregions(region_t *theregions, int indent, int p_nr_partitions, int p_free_regions, int p_nr_regions, int numbers)
1819 int r, nofree = 0;
1820 region_t *reg;
1821 reg = theregions;
1823 if((p_nr_partitions >= NR_PARTITIONS || !p_free_regions) && p_free_regions)
1824 nofree = 1;
1825 for(r = 0; r < p_nr_regions; r++, reg++) {
1826 unsigned long units;
1827 if(reg->is_used_part) {
1828 char *name;
1829 name = typ2txt(reg->used_part.sysind);
1830 printf("%*s", indent, ""); type2col(reg->used_part.sysind);
1831 if(numbers) printf("[%d] ", r);
1832 printf("In use by %-10s ", name);
1833 units = reg->used_part.size / 2;
1834 col(0);
1835 printf(" (%s)\n", prettysizeprint(units));
1836 } else {
1837 printf("%*s", indent, "");
1838 if(numbers) {
1839 if(!nofree) printf("[%d] ", r);
1840 else printf("[-] ");
1842 printf("Free space ");
1843 units = ((reg->free_sec_last - reg->free_sec_start+1))/2;
1844 printf(" (%s)\n", prettysizeprint(units));
1848 if(numbers && p_nr_partitions >= NR_PARTITIONS && p_free_regions) {
1849 printf(
1850 "\nNote: there is free space on this disk, but you can't select it,\n"
1851 "because there isn't a free slot in the partition table to use it.\n"
1852 "You can reclaim the free space by deleting an adjacent region.\n");
1855 return;
1858 #define IS_YES 3
1859 #define IS_NO 4
1860 #define IS_OTHER 5
1862 is_sure(char *fmt, ...)
1864 char yesno[10];
1865 va_list ap;
1866 va_start (ap, fmt);
1867 vprintf(fmt, ap);
1868 va_end(ap);
1869 printf(" Please enter 'yes' or 'no': ");
1870 fflush(stdout);
1871 if(!fgets(yesno, sizeof(yesno)-1, stdin)) exit(1);
1873 if (strcmp(yesno, "yes\n") == 0) return(IS_YES);
1874 if (strcmp(yesno, "no\n") == 0) return(IS_NO);
1875 return IS_OTHER;
1878 void warn(char *message)
1880 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b ! %s\n",message);
1884 may_kill_region(void)
1886 int confirmation;
1887 char line[100];
1888 int r, i;
1890 if(used_regions < 1) return 1;
1892 printf("\n -- Delete in-use region? --\n\n");
1894 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1895 printf("\nEnter the region number to delete or ENTER to continue: ");
1896 fflush(NULL);
1897 fgets(line, sizeof(line)-2, stdin);
1898 if(!isdigit(line[0]))
1899 return 1;
1901 r=atoi(line);
1902 if(r < 0 || r >= nr_regions) {
1903 printf("This choice is out of range.\n");
1904 return 0;
1907 if(!regions[r].is_used_part) {
1908 printf("This region is not in use.\n");
1909 return 0;
1912 i = regions[r].tableno;
1914 printf("\nPlease confirm that you want to delete region %d, losing all data it", r);
1915 printf("\ncontains. You're disk is not actually updated right away, but still.");
1916 printf("\n\n");
1918 do {
1919 confirmation = is_sure("Are you sure you want to continue?");
1920 if (confirmation == IS_NO) return 0;
1921 } while (confirmation != IS_YES);
1923 table[i].sysind = NO_PART;
1924 dirty = 1;
1925 regionize();
1927 /* User may go again. */
1928 return 0;
1932 region_t *
1933 select_region(void)
1935 int rn, done = 0;
1936 static char line[100];
1937 int nofree = 0;
1939 printstep(2, "Select a disk region");
1941 if(nr_regions < 1) {
1942 printf("\nNo regions found - maybe the drive is too small.\n"
1943 "Please try expert mode.\n");
1944 exit(1);
1947 if(nr_partitions >= NR_PARTITIONS || !free_regions) {
1948 if(free_regions) {
1949 nofree = 1;
1954 printf("\nPlease select the region that you want to use for the MINIX 3 setup.");
1955 printf("\nIf you select an in-use region it will be overwritten by MINIX. The");
1956 printf("\nfollowing region%s were found on the selected disk:\n\n",
1957 SORNOT(nr_regions));
1958 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1961 printf("\n");
1962 do {
1963 printf("Enter the region number to use or type 'delete': ");
1964 if(nr_regions == 1) printf(" [0] ");
1965 fflush(NULL);
1967 if(!fgets(line, sizeof(line)-2, stdin))
1968 exit(1);
1970 if (nr_regions == 1 && line[0] == '\n') {
1971 rn = 0;
1972 done = 1;
1974 else {
1975 if(strcmp(line,"delete\n") == 0) {
1976 may_kill_region();
1977 return NULL;
1980 if(sscanf(line, "%d", &rn) != 1) {
1981 warn("invalid choice");
1982 continue;
1985 if(rn < 0 || rn >= nr_regions) {
1986 warn("out of range");
1987 continue;
1990 if(nofree && !regions[rn].is_used_part) {
1991 warn("not available");
1992 continue;
1995 done = 1;
1997 } while(! done);
1999 return(&regions[rn]);
2002 void printstep(int step, char *str)
2004 int n;
2005 n = printf("\n --- Substep 3.%d: %s ---", step, str);
2006 while(n++ < 73) printf("-");
2007 printf("\n");
2010 device_t *
2011 select_disk(void)
2013 int done = 0;
2014 int i, choice, drives;
2015 static char line[500];
2016 int biosdrive = 0;
2018 printstep(1, "Select a disk to install MINIX 3");
2019 printf("\nProbing for disks. This may take a short while.");
2021 i = 0;
2022 curdev=firstdev;
2024 for(; i < MAX_DEVICES;) {
2025 printf(".");
2026 fflush(stdout);
2027 m_read('r', &biosdrive);
2028 if(device >= 0) {
2029 devices[i].dev = curdev;
2030 devices[i].free_regions = free_regions;
2031 devices[i].nr_regions = nr_regions;
2032 devices[i].nr_partitions = nr_partitions;
2033 devices[i].used_regions = used_regions;
2034 devices[i].sectors = table[0].size;
2035 curdev->biosdrive = biosdrive-1;
2036 memcpy(devices[i].regions, regions, sizeof(regions));
2037 i++;
2040 nextdevice(NULL, 1);
2041 if(curdev == firstdev)
2042 break;
2045 drives = i;
2047 if(drives < 1) {
2048 printf("\nFound no drives - can't partition.\n");
2049 exit(1);
2052 printf(" Probing done.\n");
2053 printf("The following disk%s %s found on your system:\n\n", SORNOT(drives),
2054 drives == 1 ? "was" : "were");
2056 for(i = 0; i < drives; i++) {
2057 printf(" ");
2058 printf("Disk [%d]: ", i);
2059 printf("%s, ", devices[i].dev->name);
2060 printf("%s\n", prettysizeprint(devices[i].sectors/2));
2061 printregions(devices[i].regions, 8,
2062 devices[i].nr_partitions,
2063 devices[i].free_regions,
2064 devices[i].nr_regions, 0);
2067 printf("\n");
2068 do {
2069 printf("Enter the disk number to use: ");
2070 if (drives == 1) printf("[0] ");
2071 fflush(NULL);
2072 if(!fgets(line, sizeof(line)-2, stdin))
2073 exit(1);
2074 if (line[0] == '\n' && drives == 1) {
2075 choice = 0;
2076 done = 1;
2077 } else {
2078 if(sscanf(line, "%d", &choice) != 1) {
2079 warn("choose a disk");
2080 continue;
2082 if(choice < 0 || choice >= i) {
2083 warn("out of range");
2084 continue;
2086 done = 1;
2088 } while(! done);
2089 return devices[choice].dev;
2093 scribble_region(region_t *reg, struct part_entry **pe, int *made_new)
2095 int ex, changed = 0, i;
2096 struct part_entry *newpart;
2097 if(!reg->is_used_part) {
2098 ex = reg->free_sec_last - reg->free_sec_start + 1;
2099 if(made_new) *made_new = 1;
2100 } else if(made_new) *made_new = 0;
2101 if(!reg->is_used_part) {
2102 for(i = 1; i <= NR_PARTITIONS; i++)
2103 if(table[i].sysind == NO_PART)
2104 break;
2105 if(i > NR_PARTITIONS) {
2106 /* Bug, should've been caught earlier. */
2107 printf("Couldn't find a free slot. Please try expert mode.\n");
2108 exit(1);
2110 newpart = &table[i];
2111 newpart->lowsec = reg->free_sec_start;
2112 newpart->size = reg->free_sec_last - reg->free_sec_start + 1;
2113 changed = 1;
2114 newpart->sysind = MINIX_PART;
2115 } else {
2116 newpart = &reg->used_part;
2118 *pe = newpart;
2119 changed = 1;
2120 dirty = 1;
2121 return changed;
2125 sanitycheck_failed(char *dev, struct part_entry *pe)
2127 struct part_geom part;
2128 int fd;
2129 unsigned long it_lowsec, it_secsize;
2131 if((fd = open(dev, O_RDONLY)) < 0) {
2132 perror(dev);
2133 return 1;
2136 if (ioctl(fd, DIOCGETP, &part) < 0) {
2137 fprintf(stderr, "DIOCGETP failed\n");
2138 perror(dev);
2139 return 1;
2142 if(!open_ct_ok(fd)) {
2143 printf("\nAutopart error: the disk is in use. This means that although a\n"
2144 "new table has been written, it won't be in use by the system\n"
2145 "until it's no longer in use (or a reboot is done). Just in case,\n"
2146 "I'm not going to continue. Please un-use the disk (or reboot) and try\n"
2147 "again.\n\n");
2148 return 1;
2151 close(fd);
2153 it_lowsec = (unsigned long)(part.base / SECTOR_SIZE);
2154 it_secsize = (unsigned long)(part.size / SECTOR_SIZE);
2156 if(it_lowsec != pe->lowsec || it_secsize != pe->size) {
2157 fprintf(stderr, "\nReturned and set numbers don't match up!\n");
2158 fprintf(stderr, "This can happen if the disk is still opened.\n");
2159 return 1;
2162 return 0;
2166 do_autopart(int resultfd)
2168 int confirmation;
2169 region_t *r;
2170 struct part_entry *pe;
2171 struct part_entry orig_table[1 + NR_PARTITIONS];
2172 int region, newp;
2174 nordonly = 1;
2176 do {
2177 curdev = select_disk();
2178 } while(!curdev);
2180 if(device >= 0) {
2181 close(device);
2182 device = -1;
2184 recompute0();
2186 m_read('r', NULL);
2188 memcpy(orig_table, table, sizeof(table));
2190 do {
2191 /* Show regions. */
2192 r = select_region();
2193 } while(!r); /* Back to step 2. */
2195 /* Write things. */
2196 if(scribble_region(r, &pe, &newp)) {
2197 char *name;
2198 int i, found = -1;
2199 char partbuf[100], devname[100];
2200 struct part_entry *tpe = NULL;
2202 printstep(3, "Confirm your choices");
2204 region = (int)(r-regions);
2205 /* disk = (int) (curdev-devices); */
2207 printf("\nThis is the point of no return. You have selected to install MINIX 3\n");
2208 printf("into region %d of disk %s. Please confirm that you want\n",
2209 region, curdev->name);
2210 printf("to use this selection to install MINIX 3.\n\n");
2212 do {
2213 confirmation = is_sure("Are you sure you want to continue?");
2214 if (confirmation == IS_NO) return 1;
2215 } while (confirmation != IS_YES);
2217 /* Retrieve partition number in sorted order that we
2218 * have scribbled in.
2220 sort();
2221 for(i = 1; i <= NR_PARTITIONS; i++) {
2222 int si;
2223 si = sort_order[i];
2224 if(si < 1 || si > NR_PARTITIONS) {
2225 fprintf(stderr, "Autopart internal error (out of range) (nothing written).\n");
2226 exit(1);
2228 if(table[si].lowsec == pe->lowsec) {
2229 if(found > 0) {
2230 fprintf(stderr, "Autopart internal error (part found twice) (nothing written).\n");
2231 exit(1);
2233 check_ind(&table[si]);
2234 table[si].sysind = MINIX_PART;
2235 found = i;
2236 tpe = &table[si];
2239 if(found < 1) {
2240 fprintf(stderr, "Autopart internal error (part not found) (nothing written).\n");
2241 exit(1);
2243 m_write('w', NULL);
2244 if(dirty) {
2245 fprintf(stderr, "Autopart internal error (couldn't update disk).\n");
2246 exit(1);
2248 name=strrchr(curdev->name, '/');
2249 if(!name) name = curdev->name;
2250 else name++;
2252 sprintf(partbuf, "%sp%d d%dp%d\n", name, found-1,
2253 curdev->biosdrive, found-1);
2254 sprintf(devname, "/dev/%sp%d", name, found-1);
2255 if(resultfd >= 0 && write(resultfd, partbuf, strlen(partbuf)) < strlen(partbuf)) {
2256 fprintf(stderr, "Autopart internal error (couldn't write result).\n");
2257 exit(1);
2259 if(device >= 0) {
2260 close(device);
2261 device = -1;
2264 #if 0
2265 m_dump(orig_table);
2266 printf("\n");
2267 m_dump(table);
2268 #endif
2270 if(sanitycheck_failed(devname, tpe)) {
2271 fprintf(stderr, "Autopart internal error (disk sanity check failed).\n");
2272 exit(1);
2275 if(newp) {
2276 int fd;
2277 if((fd=open(devname, O_WRONLY)) < 0) {
2278 perror(devname);
2279 } else {
2280 /* Clear any subpartitioning. */
2281 static unsigned char sub[2048];
2282 sub[510] = 0x55;
2283 sub[511] = 0xAA;
2284 write(fd, sub, sizeof(sub));
2285 close(fd);
2288 return 0;
2291 return 1;
2294 int main(int argc, char **argv)
2296 int c;
2297 int i, key;
2298 int resultfd = -1;
2300 /* autopart uses getopt() */
2301 while((c = getopt(argc, argv, "m:f:")) != EOF) {
2302 switch(c) {
2303 case 'm':
2304 min_region_mb = atoi(optarg);
2305 break;
2306 case 'f':
2307 /* Make sure old data file is gone. */
2308 unlink(optarg);
2309 if((resultfd=open(optarg, O_CREAT | O_WRONLY | O_TRUNC)) < 0) {
2310 perror(optarg);
2311 return 1;
2313 sync(); /* Make sure no old data file lingers. */
2314 break;
2315 default:
2316 fprintf(stderr, "Unknown option\n");
2317 return 1;
2321 argc -= optind;
2322 argv += optind;
2324 for (i= 0; i < argc; i++) {
2325 newdevice(argv[i], 0, 0);
2328 if (firstdev == nil) {
2329 getdevices();
2330 key= ctrl('L');
2331 } else {
2332 key= 'r';
2336 int r;
2337 if (firstdev == nil) {
2338 fprintf(stderr, "autopart couldn't find any devices.\n");
2339 return 1;
2341 r = do_autopart(resultfd);
2342 if(resultfd >= 0) { close(resultfd); }
2343 return r;
2346 exit(0);