umount: getopt return value is int, not char
[minix.git] / commands / autopart / autopart.c
blobc6fd7112ca344392a3062ea604d3e7a98455073f
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/u64.h>
31 #include <minix/com.h>
32 #include <machine/partition.h>
33 #include <termios.h>
34 #include <stdarg.h>
36 /* Declare prototype. */
37 void printstep(int step, char *message);
39 /* True if a partition is an extended partition. */
40 #define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
42 /* Minix master bootstrap code. */
43 char MASTERBOOT[] = "/usr/mdec/mbr";
45 /* Template:
46 ----first---- --geom/last-- ------sectors-----
47 Device Cyl Head Sec Cyl Head Sec Base Size Kb
48 /dev/c0d0 977 5 17
49 /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521
50 Num Sort Type
51 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440
52 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142
53 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939
54 3 p3 00 None 0 0 0 0 0 -1 0 0 0
57 #define MAXSIZE 999999999L
58 #define SECTOR_SIZE 512
59 #define DEV_FD0 0x200 /* Device number of /dev/fd0 */
60 #define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */
62 int min_region_mb = 500;
64 #define MIN_REGION_SECTORS (1024*1024*min_region_mb/SECTOR_SIZE)
66 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
67 #define arraylimit(a) ((a) + arraysize(a))
69 #define SORNOT(n) ((n) == 1 ? "" : "s")
71 /* screen colours */
72 #define COL_RED 1
73 #define COL_GREEN 2
74 #define COL_ORANGE 3
75 #define COL_BLUE 4
76 #define COL_MAGENTA 5
77 #define COL_CYAN 6
79 #define SURE_SERIOUS 1
80 #define SURE_BACK 2
82 void col(int col)
84 if(!col) printf("\033[0m");
85 else printf("\033[3%dm", col % 10);
88 void type2col(int type)
90 switch(type) {
91 /* minix */
92 case 0x80:
93 case MINIX_PART: col(COL_GREEN); break;
95 /* dos/windows */
96 case 0x0B: case 0x0C: case 0x0E: case 0x0F: case 0x42:
97 case 0x07: col(COL_CYAN); break;
99 /* linux */
100 case 0x82: case 0x83: col(COL_ORANGE); break;
104 int open_ct_ok(int fd)
106 int c = -1;
107 if(ioctl(fd, DIOCOPENCT, &c) < 0) {
108 printf("Warning: couldn't verify opencount, continuing\n");
109 return 1;
112 if(c == 1) return 1;
113 if(c < 1) { printf("Error: open count %d\n", c); }
115 return 0;
118 void report(const char *label)
120 fprintf(stderr, "part: %s: %s\n", label, strerror(errno));
123 void fatal(const char *label)
125 report(label);
126 exit(1);
129 struct termios termios;
131 void restore_ttyflags(void)
132 /* Reset the tty flags to how we got 'em. */
134 if (tcsetattr(0, TCSANOW, &termios) < 0) fatal("");
137 void tty_raw(void)
138 /* Set the terminal to raw mode, no signals, no echoing. */
140 struct termios rawterm;
142 rawterm= termios;
143 rawterm.c_lflag &= ~(ICANON|ISIG|ECHO);
144 rawterm.c_iflag &= ~(ICRNL);
145 if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal("");
148 #define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37'))
150 char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16];
151 #define STATUSROW 10
153 int putchr(int c)
155 return putchar(c);
158 void putstr(char *s)
160 int c;
162 while ((c= *s++) != 0) putchr(c);
165 void set_cursor(int row, int col)
167 tputs(tgoto(t_cm, col, row), 1, putchr);
170 int statusrow= STATUSROW;
171 int stat_ktl= 1;
172 int need_help= 1;
174 void stat_start(int serious)
175 /* Prepare for printing on a fresh status line, possibly highlighted. */
177 set_cursor(statusrow++, 0);
178 tputs(t_cd, 1, putchr);
179 if (serious) tputs(t_so, 1, putchr);
182 void stat_end(int ktl)
183 /* Closing bracket for stat_start. Sets "keystrokes to live" of message. */
185 tputs(t_se, 1, putchr);
186 stat_ktl= ktl;
187 need_help= 1;
190 void stat_reset(void)
191 /* Reset the statusline pointer and clear old messages if expired. */
193 if (stat_ktl > 0 && --stat_ktl == 0) {
194 statusrow= STATUSROW;
195 need_help= 1;
197 if (need_help && statusrow < (24-2)) {
198 if (statusrow > STATUSROW) stat_start(0);
199 stat_start(0);
200 putstr(
201 "Type '+' or '-' to change, 'r' to read, '?' for more help, '!' for advice");
203 statusrow= STATUSROW;
204 need_help= 0;
207 void clear_screen(void)
209 set_cursor(0, 0);
210 tputs(t_cd, 1, putchr);
211 stat_ktl= 1;
212 stat_reset();
215 void reset_tty(void)
216 /* Reset the tty to cooked mode. */
218 restore_ttyflags();
219 set_cursor(statusrow, 0);
220 tputs(t_cd, 1, putchr);
223 void *alloc(size_t n)
225 void *m;
227 if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); }
229 return m;
232 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
234 typedef struct device {
235 struct device *next, *prev; /* Circular dequeue. */
236 dev_t rdev; /* Device number (sorting only). */
237 char *name; /* E.g. /dev/c0d0 */
238 char *subname; /* E.g. /dev/c0d0:2 */
239 parttype_t parttype;
240 int biosdrive;
241 } device_t;
243 typedef struct region {
244 /* A region is either an existing top-level partition
245 * entry (used_part is non-NULL) or free space (free_*
246 * contains data).
248 struct part_entry used_part;
249 int is_used_part;
250 int tableno;
251 int free_sec_start, free_sec_last;
252 } region_t;
254 /* A disk has between 1 and 2*partitions+1 regions;
255 * the last case is free space before and after every partition.
257 #define NR_REGIONS (2*NR_PARTITIONS+1)
258 region_t regions[NR_REGIONS];
259 int nr_partitions = 0, nr_regions = 0, free_regions, used_regions;
260 int nordonly = 0;
262 device_t *firstdev= nil, *curdev;
264 #define MAX_DEVICES 100
265 static struct {
266 device_t *dev;
267 int nr_partitions, free_regions, used_regions, sectors, nr_regions;
268 int biosdrive;
269 region_t regions[NR_REGIONS];
270 } devices[MAX_DEVICES];
272 void newdevice(char *name, int scanning, int disk_only)
273 /* Add a device to the device list. If scanning is set then we are reading
274 * /dev, so insert the device in device number order and make /dev/c0d0 current.
277 device_t *new, *nextdev, *prevdev;
278 struct stat st;
280 st.st_rdev= 0;
281 if (scanning) {
282 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
284 switch (major(st.st_rdev)) {
285 case 3:
286 /* Disk controller */
287 if (minor(st.st_rdev) >= 0x80
288 || minor(st.st_rdev) % 5 != 0) return;
289 break;
290 default:
291 return;
293 /* Interesting device found. */
294 } else {
295 if(stat(name, &st) < 0) { perror(name); return; }
298 new= alloc(sizeof(*new));
299 new->rdev= st.st_rdev;
300 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
301 strcpy(new->name, name);
302 new->subname= new->name;
303 new->parttype= DUNNO;
304 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
305 new->parttype= FLOPPY;
306 } else
307 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
308 && minor(st.st_rdev) % 5 == 0) {
309 new->parttype= PRIMARY;
312 if (firstdev == nil) {
313 firstdev= new;
314 new->next= new->prev= new;
315 curdev= firstdev;
316 return;
318 nextdev= firstdev;
319 while (new->rdev >= nextdev->rdev
320 && (nextdev= nextdev->next) != firstdev) {}
321 prevdev= nextdev->prev;
322 new->next= nextdev;
323 nextdev->prev= new;
324 new->prev= prevdev;
325 prevdev->next= new;
327 if (new->rdev < firstdev->rdev) firstdev= new;
328 if (new->rdev == DEV_C0D0) curdev= new;
329 if (curdev->rdev != DEV_C0D0) curdev= firstdev;
332 void getdevices(void)
333 /* Get all block devices from /dev that look interesting. */
335 DIR *d;
336 struct dirent *e;
337 char name[5 + NAME_MAX + 1];
339 if ((d= opendir("/dev")) == nil) fatal("/dev");
341 while ((e= readdir(d)) != nil) {
342 strcpy(name, "/dev/");
343 strcpy(name + 5, e->d_name);
344 newdevice(name, 1, 1);
346 (void) closedir(d);
349 int dirty= 0;
350 unsigned char bootblock[SECTOR_SIZE];
351 struct part_entry table[1 + NR_PARTITIONS];
352 int existing[1 + NR_PARTITIONS];
353 unsigned long offset= 0, extbase= 0, extsize;
354 int submerged= 0;
355 int sort_index[1 + NR_PARTITIONS], sort_order[1 + NR_PARTITIONS];
356 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
357 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
358 int precise= 0;
359 int device= -1;
361 unsigned long sortbase(struct part_entry *pe)
363 return pe->sysind == NO_PART ? -1 : pe->lowsec;
366 void sort(void)
367 /* Let the sort_index array show the order partitions are sorted in. */
369 int i, j;
371 for (i= 1; i <= NR_PARTITIONS; i++) sort_order[i]= i;
373 for (i= 1; i <= NR_PARTITIONS; i++) {
374 for (j= 1; j <= NR_PARTITIONS-1; j++) {
375 int sj= sort_order[j], sj1= sort_order[j+1];
377 if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
378 sort_order[j]= sj1;
379 sort_order[j+1]= sj;
383 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[sort_order[i]]= i;
386 void dos2chs(unsigned char *dos, unsigned *chs)
387 /* Extract cylinder, head and sector from the three bytes DOS uses to address
388 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
389 * of the sector byte. The sector number is rebased to count from 0.
392 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
393 chs[1]= dos[0];
394 chs[2]= (dos[1] & 0x3F) - 1;
397 void abs2dos(unsigned char *dos, unsigned long pos)
398 /* Translate a sector offset to three DOS bytes. */
400 unsigned h, c, s;
402 c= pos / secpcyl;
403 h= (pos % secpcyl) / sectors;
404 s= pos % sectors + 1;
406 dos[0]= h;
407 dos[1]= s | ((c >> 2) & 0xC0);
408 dos[2]= c & 0xFF;
411 void recompute0(void)
412 /* Recompute the partition size for the device after a geometry change. */
414 if (device < 0) {
415 cylinders= heads= sectors= 1;
416 memset(table, 0, sizeof(table));
417 } else
418 if (!precise && offset == 0) {
419 table[0].lowsec= 0;
420 table[0].size= (unsigned long) cylinders * heads * sectors;
422 table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
423 secpcyl= heads * sectors;
426 void guess_geometry(void)
427 /* With a bit of work one can deduce the disk geometry from the partition
428 * table. This may be necessary if the driver gets it wrong. (If partition
429 * tables didn't have C/H/S numbers we would not care at all...)
432 int i, n;
433 struct part_entry *pe;
434 unsigned chs[3];
435 unsigned long sec;
436 unsigned h, s;
437 unsigned char HS[256][8]; /* Bit map off all possible H/S */
439 alt_cyls= alt_heads= alt_secs= 0;
441 /* Initially all possible H/S combinations are possible. HS[h][0]
442 * bit 0 is used to rule out a head value.
444 for (h= 1; h <= 255; h++) {
445 for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
448 for (i= 0; i < 2*NR_PARTITIONS; i++) {
449 pe= &(table+1)[i >> 1];
450 if (pe->sysind == NO_PART) continue;
452 /* Get the end or start sector numbers (in that order). */
453 if ((i & 1) == 0) {
454 dos2chs(&pe->last_head, chs);
455 sec= pe->lowsec + pe->size - 1;
456 } else {
457 dos2chs(&pe->start_head, chs);
458 sec= pe->lowsec;
461 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
463 /* Which H/S combinations can be ruled out? */
464 for (h= 1; h <= 255; h++) {
465 if (HS[h][0] == 0) continue;
466 n = 0;
467 for (s= 1; s <= 63; s++) {
468 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
469 HS[h][s/8] &= ~(1 << (s%8));
471 if (HS[h][s/8] & (1 << (s%8))) n++;
473 if (n == 0) HS[h][0]= 0;
477 /* See if only one remains. */
478 i= 0;
479 for (h= 1; h <= 255; h++) {
480 if (HS[h][0] == 0) continue;
481 for (s= 1; s <= 63; s++) {
482 if (HS[h][s/8] & (1 << (s%8))) {
483 i++;
484 alt_heads= h;
485 alt_secs= s;
490 /* Forget it if more than one choice... */
491 if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
494 void geometry(void)
495 /* Find out the geometry of the device by querying the driver, or by looking
496 * at the partition table. These numbers are crosschecked to make sure that
497 * the geometry is correct. Master bootstraps other than the Minix one use
498 * the CHS numbers in the partition table to load the bootstrap of the active
499 * partition.
502 struct stat dst;
503 int err= 0;
504 struct partition geometry;
506 if (submerged) {
507 /* Geometry already known. */
508 sort();
509 return;
511 precise= 0;
512 cylinders= 0;
513 recompute0();
514 if (device < 0) return;
516 /* Try to guess the geometry from the partition table. */
517 guess_geometry();
519 /* Try to get the geometry from the driver. */
520 (void) fstat(device, &dst);
522 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
523 /* Try to get the drive's geometry from the driver. */
525 if (ioctl(device, DIOCGETP, &geometry) < 0)
526 err= errno;
527 else {
528 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
529 table[0].size= div64u(geometry.size, SECTOR_SIZE);
530 cylinders= geometry.cylinders;
531 heads= geometry.heads;
532 sectors= geometry.sectors;
533 precise= 1;
535 } else {
536 err= ENODEV;
539 if (err != 0) {
540 /* Getting the geometry from the driver failed, so use the
541 * alternate geometry.
543 if (alt_heads == 0) {
544 alt_cyls= table[0].size / (64 * 32);
545 alt_heads= 64;
546 alt_secs= 32;
549 cylinders= alt_cyls;
550 heads= alt_heads;
551 sectors= alt_secs;
553 stat_start(1);
554 printf("Failure to get the geometry of %s: %s", curdev->name,
555 errno == ENOTTY ? "No driver support" : strerror(err));
556 stat_end(5);
557 stat_start(0);
558 printf("The geometry has been guessed as %ux%ux%u",
559 cylinders, heads, sectors);
560 stat_end(5);
561 } else {
562 if (alt_heads == 0) {
563 alt_cyls= cylinders;
564 alt_heads= heads;
565 alt_secs= sectors;
568 if (heads != alt_heads || sectors != alt_secs) {
569 printf(
570 "The geometry obtained from the driver\n"
571 "does not match the geometry implied by the partition\n"
572 "table. Please use expert mode instead.\n");
573 exit(1);
577 /* Show the base and size of the device instead of the whole drive.
578 * This makes sense for subpartitioning primary partitions.
580 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
581 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
582 table[0].size= div64u(geometry.size, SECTOR_SIZE);
583 } else {
584 precise= 0;
586 recompute0();
587 sort();
590 typedef struct indicators { /* Partition type to partition name. */
591 unsigned char ind;
592 char name[10];
593 } indicators_t;
595 indicators_t ind_table[]= {
596 { 0x00, "None" },
597 { 0x01, "FAT-12" },
598 { 0x02, "XENIX /" },
599 { 0x03, "XENIX usr" },
600 { 0x04, "FAT-16" },
601 { 0x05, "EXTENDED" },
602 { 0x06, "FAT-16" },
603 { 0x07, "HPFS/NTFS" },
604 { 0x08, "AIX" },
605 { 0x09, "COHERENT" },
606 { 0x0A, "OS/2" },
607 { 0x0B, "FAT-32" },
608 { 0x0C, "FAT?" },
609 { 0x0E, "FAT?" },
610 { 0x0F, "EXTENDED" },
611 { 0x10, "OPUS" },
612 { 0x40, "VENIX286" },
613 { 0x42, "W2000 Dyn" },
614 { 0x52, "MICROPORT" },
615 { 0x63, "386/IX" },
616 { 0x64, "NOVELL286" },
617 { 0x65, "NOVELL386" },
618 { 0x75, "PC/IX" },
619 { 0x80, "MINIX-OLD" },
620 { 0x81, "MINIX" },
621 { 0x82, "LINUXswap" },
622 { 0x83, "LINUX" },
623 { 0x93, "AMOEBA" },
624 { 0x94, "AMOEBAbad" },
625 { 0xA5, "386BSD" },
626 { 0xB7, "BSDI" },
627 { 0xB8, "BSDI swap" },
628 { 0xC7, "SYRINX" },
629 { 0xDB, "CPM" },
630 { 0xFF, "BADBLOCKS" },
633 char *typ2txt(int ind)
634 /* Translate a numeric partition indicator for human eyes. */
636 indicators_t *pind;
638 for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
639 if (pind->ind == ind) return pind->name;
641 return "unknown system";
644 int round_sysind(int ind, int delta)
645 /* Find the next known partition type starting with ind in direction delta. */
647 indicators_t *pind;
649 ind= (ind + delta) & 0xFF;
651 if (delta < 0) {
652 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
653 } else {
654 for (pind= ind_table; pind->ind < ind; pind++) {}
656 return pind->ind;
659 /* Objects on the screen, either simple pieces of the text or the cylinder
660 * number of the start of partition three.
662 typedef enum objtype {
663 O_INFO, O_TEXT, O_DEV, O_SUB,
664 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
665 O_CYL, O_HEAD, O_SEC,
666 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
667 } objtype_t;
669 #define rjust(type) ((type) >= O_TYPHEX)
670 #define computed(type) ((type) >= O_TYPTXT)
672 typedef struct object {
673 struct object *next;
674 objtype_t type; /* Text field, cylinder number, etc. */
675 char flags; /* Modifiable? */
676 char row;
677 char col;
678 char len;
679 struct part_entry *entry; /* What does the object refer to? */
680 char *text;
681 char value[20]; /* Value when printed. */
682 } object_t;
684 #define OF_MOD 0x01 /* Object value is modifiable. */
685 #define OF_ODD 0x02 /* It has a somewhat odd value. */
686 #define OF_BAD 0x04 /* Its value is no good at all. */
688 /* Events: (Keypress events are the value of the key pressed.) */
689 #define E_ENTER (-1) /* Cursor moves onto object. */
690 #define E_LEAVE (-2) /* Cursor leaves object. */
691 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
693 /* The O_SIZE objects have a dual identity. */
694 enum howend { SIZE, LAST } howend= SIZE;
696 object_t *world= nil;
697 object_t *curobj= nil;
699 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
700 /* Make a new object given a type, flags, position and length on the screen. */
702 object_t *new;
703 object_t **aop= &world;
705 new= alloc(sizeof(*new));
707 new->type= type;
708 new->flags= flags;
709 new->row= row;
710 new->col= col;
711 new->len= len;
712 new->entry= nil;
713 new->text= "";
714 new->value[0]= 0;
716 new->next= *aop;
717 *aop= new;
719 return new;
722 unsigned long entry2base(struct part_entry *pe)
723 /* Return the base sector of the partition if defined. */
725 return pe->sysind == NO_PART ? 0 : pe->lowsec;
728 unsigned long entry2last(struct part_entry *pe)
730 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
733 unsigned long entry2size(struct part_entry *pe)
735 return pe->sysind == NO_PART ? 0 : pe->size;
738 int typing; /* Set if a digit has been typed to set a value. */
739 int magic; /* Changes when using the magic key. */
741 void event(int ev, object_t *op);
743 void m_redraw(int ev, object_t *op)
744 /* Redraw the screen. */
746 object_t *op2;
748 if (ev != ctrl('L')) return;
750 clear_screen();
751 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
754 void m_toggle(int ev, object_t *op)
755 /* Toggle between the driver and alternate geometry. */
757 unsigned t;
759 if (ev != 'X') return;
760 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
761 return;
763 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
764 t= heads; heads= alt_heads; alt_heads= t;
765 t= sectors; sectors= alt_secs; alt_secs= t;
766 dirty= 1;
767 recompute0();
770 char size_last[]= "Size";
772 void m_orientation(int ev, object_t *op)
774 if (ev != ' ') return;
776 switch (howend) {
777 case SIZE:
778 howend= LAST;
779 strcpy(size_last, "Last");
780 break;
781 case LAST:
782 howend= SIZE;
783 strcpy(size_last, "Size");
787 void m_move(int ev, object_t *op)
788 /* Move to the nearest modifiably object in the intended direction. Objects
789 * on the same row or column are really near.
792 object_t *near, *op2;
793 unsigned dist, d2, dr, dc;
795 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
796 return;
798 if (device < 0) {
799 /* No device open? Then try to read first. */
800 event('r', op);
801 if (device < 0) return;
804 near= op;
805 dist= -1;
807 for (op2= world; op2 != nil; op2= op2->next) {
808 if (op2 == op || !(op2->flags & OF_MOD)) continue;
810 dr= abs(op2->row - op->row);
811 dc= abs(op2->col - op->col);
813 d2= 25*dr*dr + dc*dc;
814 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
816 switch (ev) {
817 case 'h': /* Left */
818 if (op2->col >= op->col) d2= -1;
819 break;
820 case 'j': /* Down */
821 if (op2->row <= op->row) d2= -1;
822 break;
823 case 'k': /* Up */
824 if (op2->row >= op->row) d2= -1;
825 break;
826 case 'l': /* Right */
827 if (op2->col <= op->col) d2= -1;
828 break;
829 case 'H': /* Home */
830 if (op2->type == O_DEV) d2= 0;
832 if (d2 < dist) { near= op2; dist= d2; }
834 if (near != op) event(E_LEAVE, op);
835 event(E_ENTER, near);
838 void m_updown(int ev, object_t *op)
839 /* Move a partition table entry up or down. */
841 int i, j;
842 struct part_entry tmp;
843 int tmpx;
845 if (ev != ctrl('K') && ev != ctrl('J')) return;
846 if (op->entry == nil) return;
848 i= op->entry - table;
849 if (ev == ctrl('K')) {
850 if (i <= 1) return;
851 j= i-1;
852 } else {
853 if (i >= NR_PARTITIONS) return;
854 j= i+1;
857 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
858 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
859 sort();
860 dirty= 1;
861 event(ev == ctrl('K') ? 'k' : 'j', op);
864 void m_enter(int ev, object_t *op)
865 /* We've moved onto this object. */
867 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
868 return;
869 curobj= op;
870 typing= 0;
871 magic= 0;
874 void m_leave(int ev, object_t *op)
875 /* About to leave this object. */
877 if (ev != E_LEAVE) return;
880 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
881 /* Only set *var to value if it looks reasonable. */
883 if (low <= value && value <= high) {
884 *var= value;
885 return 1;
886 } else
887 return 0;
890 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
891 unsigned long high)
893 if (low <= value && value <= high) {
894 *var= value;
895 return 1;
896 } else
897 return 0;
900 int nextdevice(object_t *op, int delta)
901 /* Select the next or previous device from the device list. */
903 dev_t rdev;
905 if (offset != 0) return 0;
906 if (dirty) event(E_WRITE, op);
907 if (dirty) return 0;
909 if (device >= 0) {
910 (void) close(device);
911 device= -1;
913 recompute0();
915 rdev= curdev->rdev;
916 if (delta < 0) {
918 curdev= curdev->prev;
919 while (delta < -1 && major(curdev->rdev) == major(rdev)
920 && curdev->rdev < rdev);
921 } else {
923 curdev= curdev->next;
924 while (delta > 1 && major(curdev->rdev) == major(rdev)
925 && curdev->rdev > rdev);
927 return 1;
930 void check_ind(struct part_entry *pe)
931 /* If there are no other partitions then make this new one active. */
933 struct part_entry *pe2;
934 int i = 0;
936 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++, i++)
937 if (pe2->sysind != NO_PART && (pe2->bootind & ACTIVE_FLAG))
938 return;
940 pe->bootind= ACTIVE_FLAG;
941 dirty = 1;
944 int check_existing(struct part_entry *pe)
945 /* Check and if not ask if an existing partition may be modified. */
947 static int expert= 0;
948 int c;
950 if (expert || pe == nil || !existing[pe - table]) return 1;
952 stat_start(1);
953 putstr("Do you wish to modify existing partitions? (y/n) ");
954 fflush(stdout);
955 while ((c= getchar()) != 'y' && c != 'n') {}
956 putchr(c);
957 stat_end(3);
958 return (expert= (c == 'y'));
961 void m_modify(int ev, object_t *op)
962 /* Increment, decrement, set, or toggle the value of an object, using
963 * arithmetic tricks the author doesn't understand either.
966 object_t *op2;
967 struct part_entry *pe= op->entry;
968 int mul, delta;
969 unsigned level= 1;
970 unsigned long surplus;
971 int radix= op->type == O_TYPHEX ? 0x10 : 10;
972 unsigned long t;
974 if (device < 0 && op->type != O_DEV) return;
976 switch (ev) {
977 case '-':
978 mul= radix; delta= -1; typing= 0;
979 break;
980 case '+':
981 mul= radix; delta= 1; typing= 0;
982 break;
983 case '\b':
984 if (!typing) return;
985 mul= 1; delta= 0;
986 break;
987 case '\r':
988 typing= 0;
989 return;
990 default:
991 if ('0' <= ev && ev <= '9')
992 delta= ev - '0';
993 else
994 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
995 delta= ev - 'a' + 10;
996 else
997 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
998 delta= ev - 'A' + 10;
999 else
1000 return;
1002 mul= typing ? radix*radix : 0;
1003 typing= 1;
1005 magic= 0;
1007 if (!check_existing(pe)) return;
1009 switch (op->type) {
1010 case O_DEV:
1011 if (ev != '-' && ev != '+') return;
1012 if (!nextdevice(op, delta)) return;
1013 break;
1014 case O_CYL:
1015 if (!within(&cylinders, 1,
1016 cylinders * mul / radix + delta, 1024)) return;
1017 recompute0();
1018 break;
1019 case O_HEAD:
1020 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1021 return;
1022 recompute0();
1023 break;
1024 case O_SEC:
1025 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1026 return;
1027 recompute0();
1028 break;
1029 case O_NUM:
1030 if (ev != '-' && ev != '+') return;
1031 for (op2= world; op2 != nil; op2= op2->next) {
1032 if (op2->type == O_NUM && ev == '+')
1033 op2->entry->bootind= 0;
1035 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1036 break;
1037 case O_TYPHEX:
1038 check_ind(pe);
1039 pe->sysind= pe->sysind * mul / radix + delta;
1040 break;
1041 case O_TYPTXT:
1042 if (ev != '-' && ev != '+') return;
1043 check_ind(pe);
1044 pe->sysind= round_sysind(pe->sysind, delta);
1045 break;
1046 case O_SCYL:
1047 level= heads;
1048 case O_SHEAD:
1049 level*= sectors;
1050 case O_SSEC:
1051 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1052 case O_BASE:
1053 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1054 t= pe->lowsec;
1055 surplus= t % level;
1056 if (!lwithin(&t, 0L,
1057 (t / level * mul / radix + delta) * level + surplus,
1058 MAXSIZE)) return;
1059 if (howend == LAST || op->type != O_BASE)
1060 pe->size-= t - pe->lowsec;
1061 pe->lowsec= t;
1062 check_ind(pe);
1063 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1064 break;
1065 case O_LCYL:
1066 level= heads;
1067 case O_LHEAD:
1068 level*= sectors;
1069 case O_LSEC:
1070 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1072 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1073 t= pe->lowsec + pe->size - 1 + level;
1074 surplus= t % level - mul / radix * level;
1075 if (!lwithin(&t, 0L,
1076 (t / level * mul / radix + delta) * level + surplus,
1077 MAXSIZE)) return;
1078 pe->size= t - pe->lowsec + 1;
1079 check_ind(pe);
1080 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1081 break;
1082 case O_KB:
1083 level= 2;
1084 if (mul == 0) pe->size= 0; /* new value, no surplus */
1085 case O_SIZE:
1086 if (pe->sysind == NO_PART) {
1087 if (op->type == O_KB || howend == SIZE) {
1088 /* First let loose magic to set the base. */
1089 event('m', op);
1090 magic= 0;
1091 pe->size= 0;
1092 event(ev, op);
1093 return;
1095 memset(pe, 0, sizeof(*pe));
1097 t= (op->type == O_KB || howend == SIZE) ? pe->size
1098 : pe->lowsec + pe->size - 1;
1099 surplus= t % level;
1100 if (!lwithin(&t, 0L,
1101 (t / level * mul / radix + delta) * level + surplus,
1102 MAXSIZE)) return;
1103 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1104 t - pe->lowsec + 1;
1105 check_ind(pe);
1106 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1107 break;
1108 default:
1109 return;
1112 /* The order among the entries may have changed. */
1113 sort();
1114 dirty= 1;
1117 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1118 int nspells;
1119 objtype_t touching;
1121 void newspell(unsigned long charm)
1122 /* Add a new spell, descending order for the base, ascending for the size. */
1124 int i, j;
1126 if (charm - table[0].lowsec > table[0].size) return;
1128 for (i= 0; i < nspells; i++) {
1129 if (charm == spell[i]) return; /* duplicate */
1131 if (touching == O_BASE) {
1132 if (charm == table[0].lowsec + table[0].size) return;
1133 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1134 } else {
1135 if (charm == table[0].lowsec) return;
1136 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1139 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1140 spell[i]= charm;
1143 void m_magic(int ev, object_t *op)
1144 /* Apply magic onto a base or size number. */
1146 struct part_entry *pe= op->entry, *pe2;
1147 int rough= (offset != 0 && extbase == 0);
1149 if (ev != 'm' || device < 0) return;
1150 typing= 0;
1152 if (!check_existing(pe)) return;
1154 if (magic == 0) {
1155 /* See what magic we can let loose on this value. */
1156 nspells= 1;
1158 /* First spell, the current value. */
1159 switch (op->type) {
1160 case O_SCYL:
1161 case O_SHEAD: /* Start of partition. */
1162 case O_SSEC:
1163 case O_BASE:
1164 touching= O_BASE;
1165 spell[0]= pe->lowsec;
1166 break;
1167 case O_LCYL:
1168 case O_LHEAD:
1169 case O_LSEC: /* End of partition. */
1170 case O_KB:
1171 case O_SIZE:
1172 touching= O_SIZE;
1173 spell[0]= pe->lowsec + pe->size;
1174 break;
1175 default:
1176 return;
1178 if (pe->sysind == NO_PART) {
1179 memset(pe, 0, sizeof(*pe));
1180 check_ind(pe);
1181 pe->sysind= MINIX_PART;
1182 spell[0]= 0;
1183 if (touching == O_SIZE) {
1184 /* First let loose magic on the base. */
1185 object_t *op2;
1187 for (op2= world; op2 != nil; op2= op2->next) {
1188 if (op2->row == op->row &&
1189 op2->type == O_BASE) {
1190 event('m', op2);
1193 magic= 0;
1194 event('m', op);
1195 return;
1198 /* Avoid the first sector on the device. */
1199 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1201 /* Further interesting values are the the bases of other
1202 * partitions or their ends.
1204 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1205 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1206 if (pe2->lowsec == table[0].lowsec)
1207 newspell(table[0].lowsec + 1);
1208 else
1209 newspell(pe2->lowsec);
1210 newspell(pe2->lowsec + pe2->size);
1211 if (touching == O_BASE && howend == SIZE) {
1212 newspell(pe2->lowsec - pe->size);
1213 newspell(pe2->lowsec + pe2->size - pe->size);
1215 if (pe2->lowsec % sectors != 0) rough= 1;
1217 /* Present values rounded up to the next cylinder unless
1218 * the table is already a mess. Use "start + 1 track" instead
1219 * of "start + 1 cylinder". Also add the end of the last
1220 * cylinder.
1222 if (!rough) {
1223 unsigned long n= spell[0];
1224 if (n == table[0].lowsec) n++;
1225 n= (n + sectors - 1) / sectors * sectors;
1226 if (n != table[0].lowsec + sectors)
1227 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1228 newspell(n);
1229 if (touching == O_SIZE)
1230 newspell(table[0].size / secpcyl * secpcyl);
1233 /* Magic has been applied, a spell needs to be chosen. */
1235 if (++magic == nspells) magic= 0;
1237 if (touching == O_BASE) {
1238 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1239 pe->lowsec= spell[magic];
1240 } else
1241 pe->size= spell[magic] - pe->lowsec;
1243 /* The order among the entries may have changed. */
1244 sort();
1245 dirty= 1;
1248 typedef struct diving {
1249 struct diving *up;
1250 struct part_entry old0;
1251 char *oldsubname;
1252 parttype_t oldparttype;
1253 unsigned long oldoffset;
1254 unsigned long oldextbase;
1255 } diving_t;
1257 diving_t *diving= nil;
1259 void m_in(int ev, object_t *op)
1260 /* Go down into a primary or extended partition. */
1262 diving_t *newdiv;
1263 struct part_entry *pe= op->entry, ext;
1264 int n;
1266 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1267 || (!(pe->sysind == MINIX_PART && offset == 0)
1268 && !ext_part(pe->sysind))
1269 || pe->size == 0) return;
1271 ext= *pe;
1272 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1274 if (dirty) event(E_WRITE, op);
1275 if (dirty) return;
1276 if (device >= 0) { close(device); device= -1; }
1278 newdiv= alloc(sizeof(*newdiv));
1279 newdiv->old0= table[0];
1280 newdiv->oldsubname= curdev->subname;
1281 newdiv->oldparttype= curdev->parttype;
1282 newdiv->oldoffset= offset;
1283 newdiv->oldextbase= extbase;
1284 newdiv->up= diving;
1285 diving= newdiv;
1287 table[0]= ext;
1289 n= strlen(diving->oldsubname);
1290 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1291 strcpy(curdev->subname, diving->oldsubname);
1292 curdev->subname[n++]= ':';
1293 curdev->subname[n++]= '0' + (pe - table - 1);
1294 curdev->subname[n]= 0;
1296 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1297 offset= ext.lowsec;
1298 if (ext_part(ext.sysind) && extbase == 0) {
1299 extbase= ext.lowsec;
1300 extsize= ext.size;
1301 curdev->parttype= DUNNO;
1304 submerged= 1;
1305 event('r', op);
1308 void m_out(int ev, object_t *op)
1309 /* Go up from an extended or subpartition table to its enclosing. */
1311 diving_t *olddiv;
1313 if (ev != '<' || diving == nil) return;
1315 if (dirty) event(E_WRITE, op);
1316 if (dirty) return;
1317 if (device >= 0) { close(device); device= -1; }
1319 olddiv= diving;
1320 diving= olddiv->up;
1322 table[0]= olddiv->old0;
1324 free(curdev->subname);
1325 curdev->subname= olddiv->oldsubname;
1327 curdev->parttype= olddiv->oldparttype;
1328 offset= olddiv->oldoffset;
1329 extbase= olddiv->oldextbase;
1331 free(olddiv);
1333 event('r', op);
1334 if (diving == nil) submerged= 0; /* We surfaced. */
1337 void installboot(unsigned char *bootblock, char *masterboot)
1338 /* Install code from a master bootstrap into a boot block. */
1340 FILE *mfp;
1341 unsigned char buf[SECTOR_SIZE];
1342 int n;
1343 char *err;
1345 if ((mfp= fopen(masterboot, "r")) == nil) {
1346 err= strerror(errno);
1347 goto m_err;
1350 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1351 if (ferror(mfp)) {
1352 err= strerror(errno);
1353 fclose(mfp);
1354 goto m_err;
1356 else if (n < 256) {
1357 err= "Is probably not a boot sector, too small";
1358 fclose(mfp);
1359 goto m_err;
1361 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1362 /* if only code, it cannot override partition table */
1363 err= "Does not fit in a boot sector";
1364 fclose(mfp);
1365 goto m_err;
1367 else if (n == SECTOR_SIZE) {
1368 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1369 err= "Is not a boot sector (bad magic)";
1370 fclose(mfp);
1371 goto m_err;
1373 n = PART_TABLE_OFF;
1376 if (n > PART_TABLE_OFF) {
1377 err= "Does not fit in a boot sector";
1378 fclose(mfp);
1379 goto m_err;
1382 memcpy(bootblock, buf, n);
1383 fclose(mfp);
1385 /* Bootstrap installed. */
1386 return;
1388 m_err:
1389 stat_start(1);
1390 printf("%s: %s", masterboot, err);
1391 stat_end(5);
1394 ssize_t boot_readwrite(int rw)
1395 /* Read (0) or write (1) the boot sector. */
1397 int r = 0;
1399 if (lseek64(device, (u64_t) offset * SECTOR_SIZE, SEEK_SET, NULL) < 0)
1400 return -1;
1402 switch (rw) {
1403 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1404 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1407 return r;
1410 int cylinderalign(region_t *reg)
1412 if(reg->is_used_part) {
1413 if(reg->used_part.lowsec != table[0].lowsec + sectors
1414 && (reg->used_part.lowsec % secpcyl)) {
1415 int extra;
1416 extra = secpcyl - (reg->used_part.lowsec % secpcyl);
1417 reg->used_part.lowsec += extra;
1418 reg->used_part.size -= extra;
1420 if((reg->used_part.size+1) % secpcyl) {
1421 reg->used_part.size -= secpcyl - ((reg->used_part.size + 1) % secpcyl);
1423 return reg->used_part.size > 0;
1426 if(reg->free_sec_start != table[0].lowsec + sectors && (reg->free_sec_start % secpcyl)) {
1427 /* Start is unaligned. Round up. */
1428 reg->free_sec_start += secpcyl - (reg->free_sec_start % secpcyl);
1430 if((reg->free_sec_last+1) % secpcyl) {
1431 /* End is unaligned. Round down. */
1432 reg->free_sec_last -= (reg->free_sec_last+1) % secpcyl;
1435 /* Return nonzero if anything remains of the region after rounding. */
1436 return reg->free_sec_last > reg->free_sec_start;
1439 void regionize(void)
1441 int free_sec, i, si;
1443 sort();
1445 free_sec = table[0].lowsec + sectors;
1447 /* Create region data used in autopart mode. */
1448 free_regions = used_regions = nr_regions = nr_partitions = 0;
1449 if(table[0].lowsec > table[sort_order[1]].lowsec &&
1450 table[sort_order[1]].sysind != NO_PART) {
1451 printf("\nSanity check failed on %s - first partition starts before disk.\n"
1452 "Please use expert mode to correct it.\n", curdev->name);
1453 exit(1);
1455 for(si = 1; si <= NR_PARTITIONS; si++) {
1456 i = sort_order[si];
1457 if(i < 1 || i > NR_PARTITIONS) {
1458 printf("Sorry, something unexpected has happened (%d out of range).\n", i);
1459 exit(1);
1462 if(table[i].sysind == NO_PART)
1463 break;
1465 /* Free space before this partition? */
1466 if(table[i].lowsec > free_sec) {
1467 /* Free region before this partition. */
1468 regions[nr_regions].free_sec_start = free_sec;
1469 regions[nr_regions].free_sec_last = table[i].lowsec-1;
1470 regions[nr_regions].is_used_part = 0;
1471 if(cylinderalign(&regions[nr_regions])) {
1472 nr_regions++;
1473 free_regions++;
1477 /* Sanity check. */
1478 if(si > 1) {
1479 if(table[i].lowsec < table[sort_order[si-1]].lowsec ||
1480 table[i].lowsec < table[sort_order[si-1]].lowsec + table[sort_order[si-1]].size) {
1481 printf("\nSanity check failed on %s - partitions overlap.\n"
1482 "Please use expert mode to correct it.\n", curdev->name);
1483 exit(1);
1486 if(table[i].size > table[0].size) {
1487 printf("\nSanity check failed on %s - partition is larger than disk.\n"
1488 "Please use expert mode to correct it.\n", curdev->name);
1489 exit(1);
1491 if(table[i].size < 1) {
1492 printf("\nSanity check failed on %s - zero-sized partition.\n"
1493 "Please use expert mode to correct it.\n", curdev->name);
1494 exit(1);
1497 /* Remember used region. */
1498 memcpy(&regions[nr_regions].used_part, &table[i], sizeof(table[i]));
1499 free_sec = table[i].lowsec+table[i].size;
1500 regions[nr_regions].is_used_part = 1;
1501 regions[nr_regions].tableno = i;
1502 nr_partitions++;
1503 nr_regions++;
1504 used_regions++;
1507 /* Special case: space after partitions. */
1508 if(free_sec < table[0].lowsec + table[0].size-1) {
1509 regions[nr_regions].free_sec_start = free_sec;
1510 regions[nr_regions].free_sec_last = table[0].lowsec + table[0].size-1;
1511 regions[nr_regions].is_used_part = 0;
1512 if(cylinderalign(&regions[nr_regions])) {
1513 nr_regions++;
1514 free_regions++;
1520 void m_read(int ev, int *biosdrive)
1521 /* Read the partition table from the current device. */
1523 int i, mode, n, v;
1524 struct part_entry *pe;
1525 u32_t system_hz;
1527 if (ev != 'r' || device >= 0) return;
1529 /* Open() may cause kernel messages: */
1530 stat_start(0);
1531 fflush(stdout);
1533 if ((device= open(curdev->name, mode= O_RDWR, 0666)) < 0) {
1534 if (device >= 0) { close(device); device= -1; }
1535 return;
1538 system_hz = (u32_t) sysconf(_SC_CLK_TCK);
1539 v = 2*system_hz;
1540 ioctl(device, DIOCTIMEOUT, &v);
1542 memset(bootblock, 0, sizeof(bootblock));
1544 n= boot_readwrite(0);
1546 if (n <= 0) stat_start(1);
1547 if (n < 0) {
1548 close(device);
1549 device= -1;
1550 } else
1551 if (n < SECTOR_SIZE) {
1552 close(device);
1553 device= -1;
1554 return;
1556 if (n <= 0) stat_end(5);
1558 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1560 if(biosdrive) (*biosdrive)++;
1562 if(!open_ct_ok(device)) {
1563 printf("\n%s: device in use! skipping it.", curdev->subname);
1564 fflush(stdout);
1565 close(device);
1566 device= -1;
1567 return;
1570 memcpy(table+1, bootblock+PART_TABLE_OFF,
1571 NR_PARTITIONS * sizeof(table[1]));
1572 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1573 /* Invalid boot block, install bootstrap, wipe partition table.
1575 memset(bootblock, 0, sizeof(bootblock));
1576 installboot(bootblock, MASTERBOOT);
1577 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1580 /* Fix an extended partition table up to something mere mortals can
1581 * understand. Record already defined partitions.
1583 for (i= 1; i <= NR_PARTITIONS; i++) {
1584 pe= &table[i];
1585 if (extbase != 0 && pe->sysind != NO_PART)
1586 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1587 existing[i]= pe->sysind != NO_PART;
1589 geometry();
1590 dirty= 0;
1592 /* Warn about grave dangers ahead. */
1593 if (extbase != 0) {
1594 stat_start(1);
1595 printf("Warning: You are in an extended partition.");
1596 stat_end(5);
1599 regionize();
1602 void m_write(int ev, object_t *op)
1603 /* Write the partition table back if modified. */
1605 struct part_entry new_table[NR_PARTITIONS], *pe;
1607 if (ev != 'w' && ev != E_WRITE) return;
1608 if (device < 0) { dirty= 0; return; }
1609 if (!dirty) {
1610 if (ev == 'w') {
1611 stat_start(1);
1612 printf("%s is not changed, or has already been written",
1613 curdev->subname);
1614 stat_end(2);
1616 return;
1619 if (extbase != 0) {
1620 /* Will this stop him? Probably not... */
1621 stat_start(1);
1622 printf("You have changed an extended partition. Bad Idea.");
1623 stat_end(5);
1626 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1627 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1628 if (pe->sysind == NO_PART) {
1629 memset(pe, 0, sizeof(*pe));
1630 } else {
1631 abs2dos(&pe->start_head, pe->lowsec);
1632 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1634 /* Fear and loathing time: */
1635 if (extbase != 0)
1636 pe->lowsec-= ext_part(pe->sysind)
1637 ? extbase : offset;
1640 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1641 bootblock[510]= 0x55;
1642 bootblock[511]= 0xAA;
1644 if (boot_readwrite(1) < 0) {
1645 stat_start(1);
1646 printf("%s: %s", curdev->name, strerror(errno));
1647 stat_end(5);
1648 return;
1650 dirty= 0;
1653 void m_shell(int ev, object_t *op)
1654 /* Shell escape, to do calculations for instance. */
1656 int r, pid, status;
1657 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1659 if (ev != 's') return;
1661 reset_tty();
1662 fflush(stdout);
1664 switch (pid= fork()) {
1665 case -1:
1666 stat_start(1);
1667 printf("can't fork: %s\n", strerror(errno));
1668 stat_end(3);
1669 break;
1670 case 0:
1671 if (device >= 0) (void) close(device);
1672 execl("/bin/sh", "sh", (char *) nil);
1673 r= errno;
1674 stat_start(1);
1675 printf("/bin/sh: %s\n", strerror(errno));
1676 stat_end(3);
1677 exit(127);
1679 sigint= signal(SIGINT, SIG_IGN);
1680 sigquit= signal(SIGQUIT, SIG_IGN);
1681 sigterm= signal(SIGTERM, SIG_IGN);
1682 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1683 (void) signal(SIGINT, sigint);
1684 (void) signal(SIGQUIT, sigquit);
1685 (void) signal(SIGTERM, sigterm);
1686 tty_raw();
1687 if (pid < 0)
1689 else
1690 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1691 stat_start(0); /* Match the stat_start in the child. */
1692 else
1693 event(ctrl('L'), op);
1696 int quitting= 0;
1698 void m_quit(int ev, object_t *op)
1699 /* Write the partition table if modified and exit. */
1701 if (ev != 'q' && ev != 'x') return;
1703 quitting= 1;
1705 if (dirty) event(E_WRITE, op);
1706 if (dirty) quitting= 0;
1709 void m_help(int ev, object_t *op)
1710 /* For people without a clue; let's hope they can find the '?' key. */
1712 static struct help {
1713 char *keys;
1714 char *what;
1715 } help[]= {
1716 { "? !", "This help / more advice!" },
1717 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1718 { "0-9 (a-f)", "Enter value" },
1719 { "hjkl (arrow keys)", "Move around" },
1720 { "CTRL-K CTRL-J", "Move entry up/down" },
1721 { "CTRL-L", "Redraw screen" },
1722 { ">", "Start a subpartition table" },
1723 { "<", "Back to the primary partition table" },
1724 { "m", "Cycle through magic values" },
1725 { "spacebar", "Show \"Size\" or \"Last\"" },
1726 { "r w", "Read/write partition table" },
1727 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1728 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1730 static char *advice[] = {
1731 "* Choose a disk with '+' and '-', then hit 'r'.",
1732 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1733 "* To make a new partition: Move over to the Size or Kb field of an unused",
1734 " partition and type the size. Hit the 'm' key to pad the partition out to",
1735 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1736 " You can hit 'm' more than once on a base or size field to see several",
1737 " interesting values go by. Note: Other Operating Systems can be picky about",
1738 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1739 " head or sector numbers.",
1740 "* To reuse a partition: Change the type to MINIX.",
1741 "* To delete a partition: Type a zero in the hex Type field.",
1742 "* To make a partition active: Type '+' in the Num field.",
1743 "* To study the list of keys: Type '?'.",
1746 if (ev == '?') {
1747 struct help *hp;
1749 for (hp= help; hp < arraylimit(help); hp++) {
1750 stat_start(0);
1751 printf("%-25s - %s", hp->keys, hp->what);
1752 stat_end(0);
1754 stat_start(0);
1755 putstr("Things like ");
1756 putstr(t_so); putstr("this"); putstr(t_se);
1757 putstr(" must be checked, but ");
1758 putstr(t_md); putstr("this"); putstr(t_me);
1759 putstr(" is not really a problem");
1760 stat_end(0);
1761 } else
1762 if (ev == '!') {
1763 char **ap;
1765 for (ap= advice; ap < arraylimit(advice); ap++) {
1766 stat_start(0);
1767 putstr(*ap);
1768 stat_end(0);
1773 void event(int ev, object_t *op)
1774 /* Simply call all modifiers for an event, each one knows when to act. */
1776 m_help(ev, op);
1777 m_redraw(ev, op);
1778 m_toggle(ev, op);
1779 m_orientation(ev, op);
1780 m_move(ev, op);
1781 m_updown(ev, op);
1782 m_enter(ev, op);
1783 m_leave(ev, op);
1784 m_modify(ev, op);
1785 m_magic(ev, op);
1786 m_in(ev, op);
1787 m_out(ev, op);
1788 m_read(ev, NULL);
1789 m_write(ev, op);
1790 m_shell(ev, op);
1791 m_quit(ev, op);
1794 char *
1795 prettysizeprint(int kb)
1797 int toosmall = 0;
1798 static char str[200];
1799 char unit = 'k';
1800 if(MIN_REGION_SECTORS > kb*2)
1801 toosmall = 1;
1802 if(kb >= 5*1024) {
1803 kb /= 1024;
1804 unit = 'M';
1805 if(kb >= 5*1024) {
1806 kb /= 1024;
1807 unit = 'G';
1810 sprintf(str, "%4d %cB%s", kb, unit,
1811 toosmall ? ", too small for MINIX 3" : "");
1812 return str;
1815 void
1816 printregions(region_t *theregions, int indent, int p_nr_partitions, int p_free_regions, int p_nr_regions, int numbers)
1818 int r, nofree = 0;
1819 region_t *reg;
1820 reg = theregions;
1822 if((p_nr_partitions >= NR_PARTITIONS || !p_free_regions) && p_free_regions)
1823 nofree = 1;
1824 for(r = 0; r < p_nr_regions; r++, reg++) {
1825 unsigned long units;
1826 if(reg->is_used_part) {
1827 char *name;
1828 name = typ2txt(reg->used_part.sysind);
1829 printf("%*s", indent, ""); type2col(reg->used_part.sysind);
1830 if(numbers) printf("[%d] ", r);
1831 printf("In use by %-10s ", name);
1832 units = reg->used_part.size / 2;
1833 col(0);
1834 printf(" (%s)\n", prettysizeprint(units));
1835 } else {
1836 printf("%*s", indent, "");
1837 if(numbers) {
1838 if(!nofree) printf("[%d] ", r);
1839 else printf("[-] ");
1841 printf("Free space ");
1842 units = ((reg->free_sec_last - reg->free_sec_start+1))/2;
1843 printf(" (%s)\n", prettysizeprint(units));
1847 if(numbers && p_nr_partitions >= NR_PARTITIONS && p_free_regions) {
1848 printf(
1849 "\nNote: there is free space on this disk, but you can't select it,\n"
1850 "because there isn't a free slot in the partition table to use it.\n"
1851 "You can reclaim the free space by deleting an adjacent region.\n");
1854 return;
1857 #define IS_YES 3
1858 #define IS_NO 4
1859 #define IS_OTHER 5
1861 is_sure(char *fmt, ...)
1863 char yesno[10];
1864 va_list ap;
1865 va_start (ap, fmt);
1866 vprintf(fmt, ap);
1867 va_end(ap);
1868 printf(" Please enter 'yes' or 'no': ");
1869 fflush(stdout);
1870 if(!fgets(yesno, sizeof(yesno)-1, stdin)) exit(1);
1872 if (strcmp(yesno, "yes\n") == 0) return(IS_YES);
1873 if (strcmp(yesno, "no\n") == 0) return(IS_NO);
1874 return IS_OTHER;
1877 void warn(char *message)
1879 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);
1883 may_kill_region(void)
1885 int confirmation;
1886 char line[100];
1887 int r, i;
1889 if(used_regions < 1) return 1;
1891 printf("\n -- Delete in-use region? --\n\n");
1893 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1894 printf("\nEnter the region number to delete or ENTER to continue: ");
1895 fflush(NULL);
1896 fgets(line, sizeof(line)-2, stdin);
1897 if(!isdigit(line[0]))
1898 return 1;
1900 r=atoi(line);
1901 if(r < 0 || r >= nr_regions) {
1902 printf("This choice is out of range.\n");
1903 return 0;
1906 if(!regions[r].is_used_part) {
1907 printf("This region is not in use.\n");
1908 return 0;
1911 i = regions[r].tableno;
1913 printf("\nPlease confirm that you want to delete region %d, losing all data it", r);
1914 printf("\ncontains. You're disk is not actually updated right away, but still.");
1915 printf("\n\n");
1917 do {
1918 confirmation = is_sure("Are you sure you want to continue?");
1919 if (confirmation == IS_NO) return 0;
1920 } while (confirmation != IS_YES);
1922 table[i].sysind = NO_PART;
1923 dirty = 1;
1924 regionize();
1926 /* User may go again. */
1927 return 0;
1931 region_t *
1932 select_region(void)
1934 int rn, done = 0;
1935 static char line[100];
1936 int nofree = 0;
1938 printstep(2, "Select a disk region");
1940 if(nr_regions < 1) {
1941 printf("\nNo regions found - maybe the drive is too small.\n"
1942 "Please try expert mode.\n");
1943 exit(1);
1946 if(nr_partitions >= NR_PARTITIONS || !free_regions) {
1947 if(free_regions) {
1948 nofree = 1;
1953 printf("\nPlease select the region that you want to use for the MINIX 3 setup.");
1954 printf("\nIf you select an in-use region it will be overwritten by MINIX. The");
1955 printf("\nfollowing region%s were found on the selected disk:\n\n",
1956 SORNOT(nr_regions));
1957 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1960 printf("\n");
1961 do {
1962 printf("Enter the region number to use or type 'delete': ");
1963 if(nr_regions == 1) printf(" [0] ");
1964 fflush(NULL);
1966 if(!fgets(line, sizeof(line)-2, stdin))
1967 exit(1);
1969 if (nr_regions == 1 && line[0] == '\n') {
1970 rn = 0;
1971 done = 1;
1973 else {
1974 if(strcmp(line,"delete\n") == 0) {
1975 may_kill_region();
1976 return NULL;
1979 if(sscanf(line, "%d", &rn) != 1) {
1980 warn("invalid choice");
1981 continue;
1984 if(rn < 0 || rn >= nr_regions) {
1985 warn("out of range");
1986 continue;
1989 if(nofree && !regions[rn].is_used_part) {
1990 warn("not available");
1991 continue;
1994 done = 1;
1996 } while(! done);
1998 return(&regions[rn]);
2001 void printstep(int step, char *str)
2003 int n;
2004 n = printf("\n --- Substep 3.%d: %s ---", step, str);
2005 while(n++ < 73) printf("-");
2006 printf("\n");
2009 device_t *
2010 select_disk(void)
2012 int done = 0;
2013 int i, choice, drives;
2014 static char line[500];
2015 int biosdrive = 0;
2017 printstep(1, "Select a disk to install MINIX 3");
2018 printf("\nProbing for disks. This may take a short while.");
2020 i = 0;
2021 curdev=firstdev;
2023 for(; i < MAX_DEVICES;) {
2024 printf(".");
2025 fflush(stdout);
2026 m_read('r', &biosdrive);
2027 if(device >= 0) {
2028 devices[i].dev = curdev;
2029 devices[i].free_regions = free_regions;
2030 devices[i].nr_regions = nr_regions;
2031 devices[i].nr_partitions = nr_partitions;
2032 devices[i].used_regions = used_regions;
2033 devices[i].sectors = table[0].size;
2034 curdev->biosdrive = biosdrive-1;
2035 memcpy(devices[i].regions, regions, sizeof(regions));
2036 i++;
2039 nextdevice(NULL, 1);
2040 if(curdev == firstdev)
2041 break;
2044 drives = i;
2046 if(drives < 1) {
2047 printf("\nFound no drives - can't partition.\n");
2048 exit(1);
2051 printf(" Probing done.\n");
2052 printf("The following disk%s %s found on your system:\n\n", SORNOT(drives),
2053 drives == 1 ? "was" : "were");
2055 for(i = 0; i < drives; i++) {
2056 printf(" ");
2057 printf("Disk [%d]: ", i);
2058 printf("%s, ", devices[i].dev->name);
2059 printf("%s\n", prettysizeprint(devices[i].sectors/2));
2060 printregions(devices[i].regions, 8,
2061 devices[i].nr_partitions,
2062 devices[i].free_regions,
2063 devices[i].nr_regions, 0);
2066 printf("\n");
2067 do {
2068 printf("Enter the disk number to use: ");
2069 if (drives == 1) printf("[0] ");
2070 fflush(NULL);
2071 if(!fgets(line, sizeof(line)-2, stdin))
2072 exit(1);
2073 if (line[0] == '\n' && drives == 1) {
2074 choice = 0;
2075 done = 1;
2076 } else {
2077 if(sscanf(line, "%d", &choice) != 1) {
2078 warn("choose a disk");
2079 continue;
2081 if(choice < 0 || choice >= i) {
2082 warn("out of range");
2083 continue;
2085 done = 1;
2087 } while(! done);
2088 return devices[choice].dev;
2092 scribble_region(region_t *reg, struct part_entry **pe, int *made_new)
2094 int ex, changed = 0, i;
2095 struct part_entry *newpart;
2096 if(!reg->is_used_part) {
2097 ex = reg->free_sec_last - reg->free_sec_start + 1;
2098 if(made_new) *made_new = 1;
2099 } else if(made_new) *made_new = 0;
2100 if(!reg->is_used_part) {
2101 for(i = 1; i <= NR_PARTITIONS; i++)
2102 if(table[i].sysind == NO_PART)
2103 break;
2104 if(i > NR_PARTITIONS) {
2105 /* Bug, should've been caught earlier. */
2106 printf("Couldn't find a free slot. Please try expert mode.\n");
2107 exit(1);
2109 newpart = &table[i];
2110 newpart->lowsec = reg->free_sec_start;
2111 newpart->size = reg->free_sec_last - reg->free_sec_start + 1;
2112 changed = 1;
2113 newpart->sysind = MINIX_PART;
2114 } else {
2115 newpart = &reg->used_part;
2117 *pe = newpart;
2118 changed = 1;
2119 dirty = 1;
2120 return changed;
2124 sanitycheck_failed(char *dev, struct part_entry *pe)
2126 struct partition part;
2127 int fd;
2128 unsigned long it_lowsec, it_secsize;
2130 if((fd = open(dev, O_RDONLY)) < 0) {
2131 perror(dev);
2132 return 1;
2135 if (ioctl(fd, DIOCGETP, &part) < 0) {
2136 fprintf(stderr, "DIOCGETP failed\n");
2137 perror(dev);
2138 return 1;
2141 if(!open_ct_ok(fd)) {
2142 printf("\nAutopart error: the disk is in use. This means that although a\n"
2143 "new table has been written, it won't be in use by the system\n"
2144 "until it's no longer in use (or a reboot is done). Just in case,\n"
2145 "I'm not going to continue. Please un-use the disk (or reboot) and try\n"
2146 "again.\n\n");
2147 return 1;
2150 close(fd);
2152 it_lowsec = div64u(part.base, SECTOR_SIZE);
2153 it_secsize = div64u(part.size, SECTOR_SIZE);
2155 if(it_lowsec != pe->lowsec || it_secsize != pe->size) {
2156 fprintf(stderr, "\nReturned and set numbers don't match up!\n");
2157 fprintf(stderr, "This can happen if the disk is still opened.\n");
2158 return 1;
2161 return 0;
2165 do_autopart(int resultfd)
2167 int confirmation;
2168 region_t *r;
2169 struct part_entry *pe;
2170 struct part_entry orig_table[1 + NR_PARTITIONS];
2171 int region, newp;
2173 nordonly = 1;
2175 do {
2176 curdev = select_disk();
2177 } while(!curdev);
2179 if(device >= 0) {
2180 close(device);
2181 device = -1;
2183 recompute0();
2185 m_read('r', NULL);
2187 memcpy(orig_table, table, sizeof(table));
2189 do {
2190 /* Show regions. */
2191 r = select_region();
2192 } while(!r); /* Back to step 2. */
2194 /* Write things. */
2195 if(scribble_region(r, &pe, &newp)) {
2196 char *name;
2197 int i, found = -1;
2198 char partbuf[100], devname[100];
2199 struct part_entry *tpe = NULL;
2201 printstep(3, "Confirm your choices");
2203 region = (int)(r-regions);
2204 /* disk = (int) (curdev-devices); */
2206 printf("\nThis is the point of no return. You have selected to install MINIX 3\n");
2207 printf("into region %d of disk %s. Please confirm that you want\n",
2208 region, curdev->name);
2209 printf("to use this selection to install MINIX 3.\n\n");
2211 do {
2212 confirmation = is_sure("Are you sure you want to continue?");
2213 if (confirmation == IS_NO) return 1;
2214 } while (confirmation != IS_YES);
2216 /* Retrieve partition number in sorted order that we
2217 * have scribbled in.
2219 sort();
2220 for(i = 1; i <= NR_PARTITIONS; i++) {
2221 int si;
2222 si = sort_order[i];
2223 if(si < 1 || si > NR_PARTITIONS) {
2224 fprintf(stderr, "Autopart internal error (out of range) (nothing written).\n");
2225 exit(1);
2227 if(table[si].lowsec == pe->lowsec) {
2228 if(found > 0) {
2229 fprintf(stderr, "Autopart internal error (part found twice) (nothing written).\n");
2230 exit(1);
2232 check_ind(&table[si]);
2233 table[si].sysind = MINIX_PART;
2234 found = i;
2235 tpe = &table[si];
2238 if(found < 1) {
2239 fprintf(stderr, "Autopart internal error (part not found) (nothing written).\n");
2240 exit(1);
2242 m_write('w', NULL);
2243 if(dirty) {
2244 fprintf(stderr, "Autopart internal error (couldn't update disk).\n");
2245 exit(1);
2247 name=strrchr(curdev->name, '/');
2248 if(!name) name = curdev->name;
2249 else name++;
2251 sprintf(partbuf, "%sp%d d%dp%d\n", name, found-1,
2252 curdev->biosdrive, found-1);
2253 sprintf(devname, "/dev/%sp%d", name, found-1);
2254 if(resultfd >= 0 && write(resultfd, partbuf, strlen(partbuf)) < strlen(partbuf)) {
2255 fprintf(stderr, "Autopart internal error (couldn't write result).\n");
2256 exit(1);
2258 if(device >= 0) {
2259 close(device);
2260 device = -1;
2263 #if 0
2264 m_dump(orig_table);
2265 printf("\n");
2266 m_dump(table);
2267 #endif
2269 if(sanitycheck_failed(devname, tpe)) {
2270 fprintf(stderr, "Autopart internal error (disk sanity check failed).\n");
2271 exit(1);
2274 if(newp) {
2275 int fd;
2276 if((fd=open(devname, O_WRONLY)) < 0) {
2277 perror(devname);
2278 } else {
2279 /* Clear any subpartitioning. */
2280 static unsigned char sub[2048];
2281 sub[510] = 0x55;
2282 sub[511] = 0xAA;
2283 write(fd, sub, sizeof(sub));
2284 close(fd);
2287 return 0;
2290 return 1;
2293 int main(int argc, char **argv)
2295 int c;
2296 int i, key;
2297 int resultfd = -1;
2299 /* autopart uses getopt() */
2300 while((c = getopt(argc, argv, "m:f:")) != EOF) {
2301 switch(c) {
2302 case 'm':
2303 min_region_mb = atoi(optarg);
2304 break;
2305 case 'f':
2306 /* Make sure old data file is gone. */
2307 unlink(optarg);
2308 if((resultfd=open(optarg, O_CREAT | O_WRONLY | O_TRUNC)) < 0) {
2309 perror(optarg);
2310 return 1;
2312 sync(); /* Make sure no old data file lingers. */
2313 break;
2314 default:
2315 fprintf(stderr, "Unknown option\n");
2316 return 1;
2320 argc -= optind;
2321 argv += optind;
2323 for (i= 0; i < argc; i++) {
2324 newdevice(argv[i], 0, 0);
2327 if (firstdev == nil) {
2328 getdevices();
2329 key= ctrl('L');
2330 } else {
2331 key= 'r';
2335 int r;
2336 if (firstdev == nil) {
2337 fprintf(stderr, "autopart couldn't find any devices.\n");
2338 return 1;
2340 r = do_autopart(resultfd);
2341 if(resultfd >= 0) { close(resultfd); }
2342 return r;
2345 exit(0);