SYSENTER/SYSCALL support
[minix.git] / commands / autopart / autopart.c
blob8e08fda08c8866963eb32cf99246d5a4efd71271
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 void putchr(int c)
155 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 #ifndef makedev /* Missing in sys/types.h */
233 #define minor(dev) (((dev) >> MINOR) & BYTE)
234 #define major(dev) (((dev) >> MAJOR) & BYTE)
235 #define makedev(major, minor) \
236 ((dev_t) (((major) << MAJOR) | ((minor) << MINOR)))
237 #endif
239 typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t;
241 typedef struct device {
242 struct device *next, *prev; /* Circular dequeue. */
243 dev_t rdev; /* Device number (sorting only). */
244 char *name; /* E.g. /dev/c0d0 */
245 char *subname; /* E.g. /dev/c0d0:2 */
246 parttype_t parttype;
247 int biosdrive;
248 } device_t;
250 typedef struct region {
251 /* A region is either an existing top-level partition
252 * entry (used_part is non-NULL) or free space (free_*
253 * contains data).
255 struct part_entry used_part;
256 int is_used_part;
257 int tableno;
258 int free_sec_start, free_sec_last;
259 } region_t;
261 /* A disk has between 1 and 2*partitions+1 regions;
262 * the last case is free space before and after every partition.
264 #define NR_REGIONS (2*NR_PARTITIONS+1)
265 region_t regions[NR_REGIONS];
266 int nr_partitions = 0, nr_regions = 0, free_regions, used_regions;
267 int nordonly = 0;
269 device_t *firstdev= nil, *curdev;
271 #define MAX_DEVICES 100
272 static struct {
273 device_t *dev;
274 int nr_partitions, free_regions, used_regions, sectors, nr_regions;
275 int biosdrive;
276 region_t regions[NR_REGIONS];
277 } devices[MAX_DEVICES];
279 void newdevice(char *name, int scanning, int disk_only)
280 /* Add a device to the device list. If scanning is set then we are reading
281 * /dev, so insert the device in device number order and make /dev/c0d0 current.
284 device_t *new, *nextdev, *prevdev;
285 struct stat st;
287 st.st_rdev= 0;
288 if (scanning) {
289 if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return;
291 switch (major(st.st_rdev)) {
292 case 3:
293 /* Disk controller */
294 if (minor(st.st_rdev) >= 0x80
295 || minor(st.st_rdev) % 5 != 0) return;
296 break;
297 default:
298 return;
300 /* Interesting device found. */
301 } else {
302 if(stat(name, &st) < 0) { perror(name); return; }
305 new= alloc(sizeof(*new));
306 new->rdev= st.st_rdev;
307 new->name= alloc((strlen(name) + 1) * sizeof(new->name[0]));
308 strcpy(new->name, name);
309 new->subname= new->name;
310 new->parttype= DUNNO;
311 if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) {
312 new->parttype= FLOPPY;
313 } else
314 if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128
315 && minor(st.st_rdev) % 5 == 0) {
316 new->parttype= PRIMARY;
319 if (firstdev == nil) {
320 firstdev= new;
321 new->next= new->prev= new;
322 curdev= firstdev;
323 return;
325 nextdev= firstdev;
326 while (new->rdev >= nextdev->rdev
327 && (nextdev= nextdev->next) != firstdev) {}
328 prevdev= nextdev->prev;
329 new->next= nextdev;
330 nextdev->prev= new;
331 new->prev= prevdev;
332 prevdev->next= new;
334 if (new->rdev < firstdev->rdev) firstdev= new;
335 if (new->rdev == DEV_C0D0) curdev= new;
336 if (curdev->rdev != DEV_C0D0) curdev= firstdev;
339 void getdevices(void)
340 /* Get all block devices from /dev that look interesting. */
342 DIR *d;
343 struct dirent *e;
344 char name[5 + NAME_MAX + 1];
346 if ((d= opendir("/dev")) == nil) fatal("/dev");
348 while ((e= readdir(d)) != nil) {
349 strcpy(name, "/dev/");
350 strcpy(name + 5, e->d_name);
351 newdevice(name, 1, 1);
353 (void) closedir(d);
356 int dirty= 0;
357 unsigned char bootblock[SECTOR_SIZE];
358 struct part_entry table[1 + NR_PARTITIONS];
359 int existing[1 + NR_PARTITIONS];
360 unsigned long offset= 0, extbase= 0, extsize;
361 int submerged= 0;
362 char sort_index[1 + NR_PARTITIONS], sort_order[1 + NR_PARTITIONS];
363 unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1;
364 unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1;
365 int precise= 0;
366 int device= -1;
368 unsigned long sortbase(struct part_entry *pe)
370 return pe->sysind == NO_PART ? -1 : pe->lowsec;
373 void sort(void)
374 /* Let the sort_index array show the order partitions are sorted in. */
376 int i, j;
378 for (i= 1; i <= NR_PARTITIONS; i++) sort_order[i]= i;
380 for (i= 1; i <= NR_PARTITIONS; i++) {
381 for (j= 1; j <= NR_PARTITIONS-1; j++) {
382 int sj= sort_order[j], sj1= sort_order[j+1];
384 if (sortbase(&table[sj]) > sortbase(&table[sj1])) {
385 sort_order[j]= sj1;
386 sort_order[j+1]= sj;
390 for (i= 1; i <= NR_PARTITIONS; i++) sort_index[sort_order[i]]= i;
393 void dos2chs(unsigned char *dos, unsigned *chs)
394 /* Extract cylinder, head and sector from the three bytes DOS uses to address
395 * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7
396 * of the sector byte. The sector number is rebased to count from 0.
399 chs[0]= ((dos[1] & 0xC0) << 2) | dos[2];
400 chs[1]= dos[0];
401 chs[2]= (dos[1] & 0x3F) - 1;
404 void abs2dos(unsigned char *dos, unsigned long pos)
405 /* Translate a sector offset to three DOS bytes. */
407 unsigned h, c, s;
409 c= pos / secpcyl;
410 h= (pos % secpcyl) / sectors;
411 s= pos % sectors + 1;
413 dos[0]= h;
414 dos[1]= s | ((c >> 2) & 0xC0);
415 dos[2]= c & 0xFF;
418 void recompute0(void)
419 /* Recompute the partition size for the device after a geometry change. */
421 if (device < 0) {
422 cylinders= heads= sectors= 1;
423 memset(table, 0, sizeof(table));
424 } else
425 if (!precise && offset == 0) {
426 table[0].lowsec= 0;
427 table[0].size= (unsigned long) cylinders * heads * sectors;
429 table[0].sysind= device < 0 ? NO_PART : MINIX_PART;
430 secpcyl= heads * sectors;
433 void guess_geometry(void)
434 /* With a bit of work one can deduce the disk geometry from the partition
435 * table. This may be necessary if the driver gets it wrong. (If partition
436 * tables didn't have C/H/S numbers we would not care at all...)
439 int i, n;
440 struct part_entry *pe;
441 unsigned chs[3];
442 unsigned long sec;
443 unsigned h, s;
444 unsigned char HS[256][8]; /* Bit map off all possible H/S */
446 alt_cyls= alt_heads= alt_secs= 0;
448 /* Initially all possible H/S combinations are possible. HS[h][0]
449 * bit 0 is used to rule out a head value.
451 for (h= 1; h <= 255; h++) {
452 for (s= 0; s < 8; s++) HS[h][s]= 0xFF;
455 for (i= 0; i < 2*NR_PARTITIONS; i++) {
456 pe= &(table+1)[i >> 1];
457 if (pe->sysind == NO_PART) continue;
459 /* Get the end or start sector numbers (in that order). */
460 if ((i & 1) == 0) {
461 dos2chs(&pe->last_head, chs);
462 sec= pe->lowsec + pe->size - 1;
463 } else {
464 dos2chs(&pe->start_head, chs);
465 sec= pe->lowsec;
468 if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1;
470 /* Which H/S combinations can be ruled out? */
471 for (h= 1; h <= 255; h++) {
472 if (HS[h][0] == 0) continue;
473 n = 0;
474 for (s= 1; s <= 63; s++) {
475 if ((chs[0] * h + chs[1]) * s + chs[2] != sec) {
476 HS[h][s/8] &= ~(1 << (s%8));
478 if (HS[h][s/8] & (1 << (s%8))) n++;
480 if (n == 0) HS[h][0]= 0;
484 /* See if only one remains. */
485 i= 0;
486 for (h= 1; h <= 255; h++) {
487 if (HS[h][0] == 0) continue;
488 for (s= 1; s <= 63; s++) {
489 if (HS[h][s/8] & (1 << (s%8))) {
490 i++;
491 alt_heads= h;
492 alt_secs= s;
497 /* Forget it if more than one choice... */
498 if (i > 1) alt_cyls= alt_heads= alt_secs= 0;
501 void geometry(void)
502 /* Find out the geometry of the device by querying the driver, or by looking
503 * at the partition table. These numbers are crosschecked to make sure that
504 * the geometry is correct. Master bootstraps other than the Minix one use
505 * the CHS numbers in the partition table to load the bootstrap of the active
506 * partition.
509 struct stat dst;
510 int err= 0;
511 struct partition geometry;
513 if (submerged) {
514 /* Geometry already known. */
515 sort();
516 return;
518 precise= 0;
519 cylinders= 0;
520 recompute0();
521 if (device < 0) return;
523 /* Try to guess the geometry from the partition table. */
524 guess_geometry();
526 /* Try to get the geometry from the driver. */
527 (void) fstat(device, &dst);
529 if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) {
530 /* Try to get the drive's geometry from the driver. */
532 if (ioctl(device, DIOCGETP, &geometry) < 0)
533 err= errno;
534 else {
535 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
536 table[0].size= div64u(geometry.size, SECTOR_SIZE);
537 cylinders= geometry.cylinders;
538 heads= geometry.heads;
539 sectors= geometry.sectors;
540 precise= 1;
542 } else {
543 err= ENODEV;
546 if (err != 0) {
547 /* Getting the geometry from the driver failed, so use the
548 * alternate geometry.
550 if (alt_heads == 0) {
551 alt_cyls= table[0].size / (64 * 32);
552 alt_heads= 64;
553 alt_secs= 32;
556 cylinders= alt_cyls;
557 heads= alt_heads;
558 sectors= alt_secs;
560 stat_start(1);
561 printf("Failure to get the geometry of %s: %s", curdev->name,
562 errno == ENOTTY ? "No driver support" : strerror(err));
563 stat_end(5);
564 stat_start(0);
565 printf("The geometry has been guessed as %ux%ux%u",
566 cylinders, heads, sectors);
567 stat_end(5);
568 } else {
569 if (alt_heads == 0) {
570 alt_cyls= cylinders;
571 alt_heads= heads;
572 alt_secs= sectors;
575 if (heads != alt_heads || sectors != alt_secs) {
576 printf(
577 "The geometry obtained from the driver\n"
578 "does not match the geometry implied by the partition\n"
579 "table. Please use expert mode instead.\n");
580 exit(1);
584 /* Show the base and size of the device instead of the whole drive.
585 * This makes sense for subpartitioning primary partitions.
587 if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) {
588 table[0].lowsec= div64u(geometry.base, SECTOR_SIZE);
589 table[0].size= div64u(geometry.size, SECTOR_SIZE);
590 } else {
591 precise= 0;
593 recompute0();
594 sort();
597 typedef struct indicators { /* Partition type to partition name. */
598 unsigned char ind;
599 char name[10];
600 } indicators_t;
602 indicators_t ind_table[]= {
603 { 0x00, "None" },
604 { 0x01, "FAT-12" },
605 { 0x02, "XENIX /" },
606 { 0x03, "XENIX usr" },
607 { 0x04, "FAT-16" },
608 { 0x05, "EXTENDED" },
609 { 0x06, "FAT-16" },
610 { 0x07, "HPFS/NTFS" },
611 { 0x08, "AIX" },
612 { 0x09, "COHERENT" },
613 { 0x0A, "OS/2" },
614 { 0x0B, "FAT-32" },
615 { 0x0C, "FAT?" },
616 { 0x0E, "FAT?" },
617 { 0x0F, "EXTENDED" },
618 { 0x10, "OPUS" },
619 { 0x40, "VENIX286" },
620 { 0x42, "W2000 Dyn" },
621 { 0x52, "MICROPORT" },
622 { 0x63, "386/IX" },
623 { 0x64, "NOVELL286" },
624 { 0x65, "NOVELL386" },
625 { 0x75, "PC/IX" },
626 { 0x80, "MINIX-OLD" },
627 { 0x81, "MINIX" },
628 { 0x82, "LINUXswap" },
629 { 0x83, "LINUX" },
630 { 0x93, "AMOEBA" },
631 { 0x94, "AMOEBAbad" },
632 { 0xA5, "386BSD" },
633 { 0xB7, "BSDI" },
634 { 0xB8, "BSDI swap" },
635 { 0xC7, "SYRINX" },
636 { 0xDB, "CPM" },
637 { 0xFF, "BADBLOCKS" },
640 char *typ2txt(int ind)
641 /* Translate a numeric partition indicator for human eyes. */
643 indicators_t *pind;
645 for (pind= ind_table; pind < arraylimit(ind_table); pind++) {
646 if (pind->ind == ind) return pind->name;
648 return "unknown system";
651 int round_sysind(int ind, int delta)
652 /* Find the next known partition type starting with ind in direction delta. */
654 indicators_t *pind;
656 ind= (ind + delta) & 0xFF;
658 if (delta < 0) {
659 for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {}
660 } else {
661 for (pind= ind_table; pind->ind < ind; pind++) {}
663 return pind->ind;
666 /* Objects on the screen, either simple pieces of the text or the cylinder
667 * number of the start of partition three.
669 typedef enum objtype {
670 O_INFO, O_TEXT, O_DEV, O_SUB,
671 O_TYPTXT, O_SORT, O_NUM, O_TYPHEX,
672 O_CYL, O_HEAD, O_SEC,
673 O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB
674 } objtype_t;
676 #define rjust(type) ((type) >= O_TYPHEX)
677 #define computed(type) ((type) >= O_TYPTXT)
679 typedef struct object {
680 struct object *next;
681 objtype_t type; /* Text field, cylinder number, etc. */
682 char flags; /* Modifiable? */
683 char row;
684 char col;
685 char len;
686 struct part_entry *entry; /* What does the object refer to? */
687 char *text;
688 char value[20]; /* Value when printed. */
689 } object_t;
691 #define OF_MOD 0x01 /* Object value is modifiable. */
692 #define OF_ODD 0x02 /* It has a somewhat odd value. */
693 #define OF_BAD 0x04 /* Its value is no good at all. */
695 /* Events: (Keypress events are the value of the key pressed.) */
696 #define E_ENTER (-1) /* Cursor moves onto object. */
697 #define E_LEAVE (-2) /* Cursor leaves object. */
698 #define E_WRITE (-3) /* Write, but not by typing 'w'. */
700 /* The O_SIZE objects have a dual identity. */
701 enum howend { SIZE, LAST } howend= SIZE;
703 object_t *world= nil;
704 object_t *curobj= nil;
706 object_t *newobject(objtype_t type, int flags, int row, int col, int len)
707 /* Make a new object given a type, flags, position and length on the screen. */
709 object_t *new;
710 object_t **aop= &world;
712 new= alloc(sizeof(*new));
714 new->type= type;
715 new->flags= flags;
716 new->row= row;
717 new->col= col;
718 new->len= len;
719 new->entry= nil;
720 new->text= "";
721 new->value[0]= 0;
723 new->next= *aop;
724 *aop= new;
726 return new;
729 unsigned long entry2base(struct part_entry *pe)
730 /* Return the base sector of the partition if defined. */
732 return pe->sysind == NO_PART ? 0 : pe->lowsec;
735 unsigned long entry2last(struct part_entry *pe)
737 return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1;
740 unsigned long entry2size(struct part_entry *pe)
742 return pe->sysind == NO_PART ? 0 : pe->size;
745 int typing; /* Set if a digit has been typed to set a value. */
746 int magic; /* Changes when using the magic key. */
748 void event(int ev, object_t *op);
750 void m_redraw(int ev, object_t *op)
751 /* Redraw the screen. */
753 object_t *op2;
755 if (ev != ctrl('L')) return;
757 clear_screen();
758 for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0;
761 void m_toggle(int ev, object_t *op)
762 /* Toggle between the driver and alternate geometry. */
764 unsigned t;
766 if (ev != 'X') return;
767 if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors)
768 return;
770 t= cylinders; cylinders= alt_cyls; alt_cyls= t;
771 t= heads; heads= alt_heads; alt_heads= t;
772 t= sectors; sectors= alt_secs; alt_secs= t;
773 dirty= 1;
774 recompute0();
777 char size_last[]= "Size";
779 void m_orientation(int ev, object_t *op)
781 if (ev != ' ') return;
783 switch (howend) {
784 case SIZE:
785 howend= LAST;
786 strcpy(size_last, "Last");
787 break;
788 case LAST:
789 howend= SIZE;
790 strcpy(size_last, "Size");
794 void m_move(int ev, object_t *op)
795 /* Move to the nearest modifiably object in the intended direction. Objects
796 * on the same row or column are really near.
799 object_t *near, *op2;
800 unsigned dist, d2, dr, dc;
802 if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H')
803 return;
805 if (device < 0) {
806 /* No device open? Then try to read first. */
807 event('r', op);
808 if (device < 0) return;
811 near= op;
812 dist= -1;
814 for (op2= world; op2 != nil; op2= op2->next) {
815 if (op2 == op || !(op2->flags & OF_MOD)) continue;
817 dr= abs(op2->row - op->row);
818 dc= abs(op2->col - op->col);
820 d2= 25*dr*dr + dc*dc;
821 if (op2->row != op->row && op2->col != op->col) d2+= 1000;
823 switch (ev) {
824 case 'h': /* Left */
825 if (op2->col >= op->col) d2= -1;
826 break;
827 case 'j': /* Down */
828 if (op2->row <= op->row) d2= -1;
829 break;
830 case 'k': /* Up */
831 if (op2->row >= op->row) d2= -1;
832 break;
833 case 'l': /* Right */
834 if (op2->col <= op->col) d2= -1;
835 break;
836 case 'H': /* Home */
837 if (op2->type == O_DEV) d2= 0;
839 if (d2 < dist) { near= op2; dist= d2; }
841 if (near != op) event(E_LEAVE, op);
842 event(E_ENTER, near);
845 void m_updown(int ev, object_t *op)
846 /* Move a partition table entry up or down. */
848 int i, j;
849 struct part_entry tmp;
850 int tmpx;
852 if (ev != ctrl('K') && ev != ctrl('J')) return;
853 if (op->entry == nil) return;
855 i= op->entry - table;
856 if (ev == ctrl('K')) {
857 if (i <= 1) return;
858 j= i-1;
859 } else {
860 if (i >= NR_PARTITIONS) return;
861 j= i+1;
864 tmp= table[i]; table[i]= table[j]; table[j]= tmp;
865 tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx;
866 sort();
867 dirty= 1;
868 event(ev == ctrl('K') ? 'k' : 'j', op);
871 void m_enter(int ev, object_t *op)
872 /* We've moved onto this object. */
874 if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X')
875 return;
876 curobj= op;
877 typing= 0;
878 magic= 0;
881 void m_leave(int ev, object_t *op)
882 /* About to leave this object. */
884 if (ev != E_LEAVE) return;
887 int within(unsigned *var, unsigned low, unsigned value, unsigned high)
888 /* Only set *var to value if it looks reasonable. */
890 if (low <= value && value <= high) {
891 *var= value;
892 return 1;
893 } else
894 return 0;
897 int lwithin(unsigned long *var, unsigned long low, unsigned long value,
898 unsigned long high)
900 if (low <= value && value <= high) {
901 *var= value;
902 return 1;
903 } else
904 return 0;
907 int nextdevice(object_t *op, int delta)
908 /* Select the next or previous device from the device list. */
910 dev_t rdev;
912 if (offset != 0) return 0;
913 if (dirty) event(E_WRITE, op);
914 if (dirty) return 0;
916 if (device >= 0) {
917 (void) close(device);
918 device= -1;
920 recompute0();
922 rdev= curdev->rdev;
923 if (delta < 0) {
925 curdev= curdev->prev;
926 while (delta < -1 && major(curdev->rdev) == major(rdev)
927 && curdev->rdev < rdev);
928 } else {
930 curdev= curdev->next;
931 while (delta > 1 && major(curdev->rdev) == major(rdev)
932 && curdev->rdev > rdev);
934 return 1;
937 void check_ind(struct part_entry *pe)
938 /* If there are no other partitions then make this new one active. */
940 struct part_entry *pe2;
941 int i = 0;
943 for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++, i++)
944 if (pe2->sysind != NO_PART && (pe2->bootind & ACTIVE_FLAG))
945 return;
947 pe->bootind= ACTIVE_FLAG;
948 dirty = 1;
951 int check_existing(struct part_entry *pe)
952 /* Check and if not ask if an existing partition may be modified. */
954 static int expert= 0;
955 int c;
957 if (expert || pe == nil || !existing[pe - table]) return 1;
959 stat_start(1);
960 putstr("Do you wish to modify existing partitions? (y/n) ");
961 fflush(stdout);
962 while ((c= getchar()) != 'y' && c != 'n') {}
963 putchr(c);
964 stat_end(3);
965 return (expert= (c == 'y'));
968 void m_modify(int ev, object_t *op)
969 /* Increment, decrement, set, or toggle the value of an object, using
970 * arithmetic tricks the author doesn't understand either.
973 object_t *op2;
974 struct part_entry *pe= op->entry;
975 int mul, delta;
976 unsigned level= 1;
977 unsigned long surplus;
978 int radix= op->type == O_TYPHEX ? 0x10 : 10;
979 unsigned long t;
981 if (device < 0 && op->type != O_DEV) return;
983 switch (ev) {
984 case '-':
985 mul= radix; delta= -1; typing= 0;
986 break;
987 case '+':
988 mul= radix; delta= 1; typing= 0;
989 break;
990 case '\b':
991 if (!typing) return;
992 mul= 1; delta= 0;
993 break;
994 case '\r':
995 typing= 0;
996 return;
997 default:
998 if ('0' <= ev && ev <= '9')
999 delta= ev - '0';
1000 else
1001 if (radix == 0x10 && 'a' <= ev && ev <= 'f')
1002 delta= ev - 'a' + 10;
1003 else
1004 if (radix == 0x10 && 'A' <= ev && ev <= 'F')
1005 delta= ev - 'A' + 10;
1006 else
1007 return;
1009 mul= typing ? radix*radix : 0;
1010 typing= 1;
1012 magic= 0;
1014 if (!check_existing(pe)) return;
1016 switch (op->type) {
1017 case O_DEV:
1018 if (ev != '-' && ev != '+') return;
1019 if (!nextdevice(op, delta)) return;
1020 break;
1021 case O_CYL:
1022 if (!within(&cylinders, 1,
1023 cylinders * mul / radix + delta, 1024)) return;
1024 recompute0();
1025 break;
1026 case O_HEAD:
1027 if (!within(&heads, 1, heads * mul / radix + delta, 255))
1028 return;
1029 recompute0();
1030 break;
1031 case O_SEC:
1032 if (!within(&sectors, 1, sectors * mul / radix + delta, 63))
1033 return;
1034 recompute0();
1035 break;
1036 case O_NUM:
1037 if (ev != '-' && ev != '+') return;
1038 for (op2= world; op2 != nil; op2= op2->next) {
1039 if (op2->type == O_NUM && ev == '+')
1040 op2->entry->bootind= 0;
1042 op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0;
1043 break;
1044 case O_TYPHEX:
1045 check_ind(pe);
1046 pe->sysind= pe->sysind * mul / radix + delta;
1047 break;
1048 case O_TYPTXT:
1049 if (ev != '-' && ev != '+') return;
1050 check_ind(pe);
1051 pe->sysind= round_sysind(pe->sysind, delta);
1052 break;
1053 case O_SCYL:
1054 level= heads;
1055 case O_SHEAD:
1056 level*= sectors;
1057 case O_SSEC:
1058 if (op->type != O_SCYL && ev != '-' && ev != '+') return;
1059 case O_BASE:
1060 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1061 t= pe->lowsec;
1062 surplus= t % level;
1063 if (!lwithin(&t, 0L,
1064 (t / level * mul / radix + delta) * level + surplus,
1065 MAXSIZE)) return;
1066 if (howend == LAST || op->type != O_BASE)
1067 pe->size-= t - pe->lowsec;
1068 pe->lowsec= t;
1069 check_ind(pe);
1070 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1071 break;
1072 case O_LCYL:
1073 level= heads;
1074 case O_LHEAD:
1075 level*= sectors;
1076 case O_LSEC:
1077 if (op->type != O_LCYL && ev != '-' && ev != '+') return;
1079 if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe));
1080 t= pe->lowsec + pe->size - 1 + level;
1081 surplus= t % level - mul / radix * level;
1082 if (!lwithin(&t, 0L,
1083 (t / level * mul / radix + delta) * level + surplus,
1084 MAXSIZE)) return;
1085 pe->size= t - pe->lowsec + 1;
1086 check_ind(pe);
1087 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1088 break;
1089 case O_KB:
1090 level= 2;
1091 if (mul == 0) pe->size= 0; /* new value, no surplus */
1092 case O_SIZE:
1093 if (pe->sysind == NO_PART) {
1094 if (op->type == O_KB || howend == SIZE) {
1095 /* First let loose magic to set the base. */
1096 event('m', op);
1097 magic= 0;
1098 pe->size= 0;
1099 event(ev, op);
1100 return;
1102 memset(pe, 0, sizeof(*pe));
1104 t= (op->type == O_KB || howend == SIZE) ? pe->size
1105 : pe->lowsec + pe->size - 1;
1106 surplus= t % level;
1107 if (!lwithin(&t, 0L,
1108 (t / level * mul / radix + delta) * level + surplus,
1109 MAXSIZE)) return;
1110 pe->size= (op->type == O_KB || howend == SIZE) ? t :
1111 t - pe->lowsec + 1;
1112 check_ind(pe);
1113 if (pe->sysind == NO_PART) pe->sysind= MINIX_PART;
1114 break;
1115 default:
1116 return;
1119 /* The order among the entries may have changed. */
1120 sort();
1121 dirty= 1;
1124 unsigned long spell[3 + 4 * (1+NR_PARTITIONS)];
1125 int nspells;
1126 objtype_t touching;
1128 void newspell(unsigned long charm)
1129 /* Add a new spell, descending order for the base, ascending for the size. */
1131 int i, j;
1133 if (charm - table[0].lowsec > table[0].size) return;
1135 for (i= 0; i < nspells; i++) {
1136 if (charm == spell[i]) return; /* duplicate */
1138 if (touching == O_BASE) {
1139 if (charm == table[0].lowsec + table[0].size) return;
1140 if ((spell[0] - charm) < (spell[0] - spell[i])) break;
1141 } else {
1142 if (charm == table[0].lowsec) return;
1143 if ((charm - spell[0]) < (spell[i] - spell[0])) break;
1146 for (j= ++nspells; j > i; j--) spell[j]= spell[j-1];
1147 spell[i]= charm;
1150 void m_magic(int ev, object_t *op)
1151 /* Apply magic onto a base or size number. */
1153 struct part_entry *pe= op->entry, *pe2;
1154 int rough= (offset != 0 && extbase == 0);
1156 if (ev != 'm' || device < 0) return;
1157 typing= 0;
1159 if (!check_existing(pe)) return;
1161 if (magic == 0) {
1162 /* See what magic we can let loose on this value. */
1163 nspells= 1;
1165 /* First spell, the current value. */
1166 switch (op->type) {
1167 case O_SCYL:
1168 case O_SHEAD: /* Start of partition. */
1169 case O_SSEC:
1170 case O_BASE:
1171 touching= O_BASE;
1172 spell[0]= pe->lowsec;
1173 break;
1174 case O_LCYL:
1175 case O_LHEAD:
1176 case O_LSEC: /* End of partition. */
1177 case O_KB:
1178 case O_SIZE:
1179 touching= O_SIZE;
1180 spell[0]= pe->lowsec + pe->size;
1181 break;
1182 default:
1183 return;
1185 if (pe->sysind == NO_PART) {
1186 memset(pe, 0, sizeof(*pe));
1187 check_ind(pe);
1188 pe->sysind= MINIX_PART;
1189 spell[0]= 0;
1190 if (touching == O_SIZE) {
1191 /* First let loose magic on the base. */
1192 object_t *op2;
1194 for (op2= world; op2 != nil; op2= op2->next) {
1195 if (op2->row == op->row &&
1196 op2->type == O_BASE) {
1197 event('m', op2);
1200 magic= 0;
1201 event('m', op);
1202 return;
1205 /* Avoid the first sector on the device. */
1206 if (spell[0] == table[0].lowsec) newspell(spell[0] + 1);
1208 /* Further interesting values are the the bases of other
1209 * partitions or their ends.
1211 for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) {
1212 if (pe2 == pe || pe2->sysind == NO_PART) continue;
1213 if (pe2->lowsec == table[0].lowsec)
1214 newspell(table[0].lowsec + 1);
1215 else
1216 newspell(pe2->lowsec);
1217 newspell(pe2->lowsec + pe2->size);
1218 if (touching == O_BASE && howend == SIZE) {
1219 newspell(pe2->lowsec - pe->size);
1220 newspell(pe2->lowsec + pe2->size - pe->size);
1222 if (pe2->lowsec % sectors != 0) rough= 1;
1224 /* Present values rounded up to the next cylinder unless
1225 * the table is already a mess. Use "start + 1 track" instead
1226 * of "start + 1 cylinder". Also add the end of the last
1227 * cylinder.
1229 if (!rough) {
1230 unsigned long n= spell[0];
1231 if (n == table[0].lowsec) n++;
1232 n= (n + sectors - 1) / sectors * sectors;
1233 if (n != table[0].lowsec + sectors)
1234 n= (n + secpcyl - 1) / secpcyl * secpcyl;
1235 newspell(n);
1236 if (touching == O_SIZE)
1237 newspell(table[0].size / secpcyl * secpcyl);
1240 /* Magic has been applied, a spell needs to be chosen. */
1242 if (++magic == nspells) magic= 0;
1244 if (touching == O_BASE) {
1245 if (howend == LAST) pe->size-= spell[magic] - pe->lowsec;
1246 pe->lowsec= spell[magic];
1247 } else
1248 pe->size= spell[magic] - pe->lowsec;
1250 /* The order among the entries may have changed. */
1251 sort();
1252 dirty= 1;
1255 typedef struct diving {
1256 struct diving *up;
1257 struct part_entry old0;
1258 char *oldsubname;
1259 parttype_t oldparttype;
1260 unsigned long oldoffset;
1261 unsigned long oldextbase;
1262 } diving_t;
1264 diving_t *diving= nil;
1266 void m_in(int ev, object_t *op)
1267 /* Go down into a primary or extended partition. */
1269 diving_t *newdiv;
1270 struct part_entry *pe= op->entry, ext;
1271 int n;
1273 if (ev != '>' || device < 0 || pe == nil || pe == &table[0]
1274 || (!(pe->sysind == MINIX_PART && offset == 0)
1275 && !ext_part(pe->sysind))
1276 || pe->size == 0) return;
1278 ext= *pe;
1279 if (extbase != 0) ext.size= extbase + extsize - ext.lowsec;
1281 if (dirty) event(E_WRITE, op);
1282 if (dirty) return;
1283 if (device >= 0) { close(device); device= -1; }
1285 newdiv= alloc(sizeof(*newdiv));
1286 newdiv->old0= table[0];
1287 newdiv->oldsubname= curdev->subname;
1288 newdiv->oldparttype= curdev->parttype;
1289 newdiv->oldoffset= offset;
1290 newdiv->oldextbase= extbase;
1291 newdiv->up= diving;
1292 diving= newdiv;
1294 table[0]= ext;
1296 n= strlen(diving->oldsubname);
1297 curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0]));
1298 strcpy(curdev->subname, diving->oldsubname);
1299 curdev->subname[n++]= ':';
1300 curdev->subname[n++]= '0' + (pe - table - 1);
1301 curdev->subname[n]= 0;
1303 curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO;
1304 offset= ext.lowsec;
1305 if (ext_part(ext.sysind) && extbase == 0) {
1306 extbase= ext.lowsec;
1307 extsize= ext.size;
1308 curdev->parttype= DUNNO;
1311 submerged= 1;
1312 event('r', op);
1315 void m_out(int ev, object_t *op)
1316 /* Go up from an extended or subpartition table to its enclosing. */
1318 diving_t *olddiv;
1320 if (ev != '<' || diving == nil) return;
1322 if (dirty) event(E_WRITE, op);
1323 if (dirty) return;
1324 if (device >= 0) { close(device); device= -1; }
1326 olddiv= diving;
1327 diving= olddiv->up;
1329 table[0]= olddiv->old0;
1331 free(curdev->subname);
1332 curdev->subname= olddiv->oldsubname;
1334 curdev->parttype= olddiv->oldparttype;
1335 offset= olddiv->oldoffset;
1336 extbase= olddiv->oldextbase;
1338 free(olddiv);
1340 event('r', op);
1341 if (diving == nil) submerged= 0; /* We surfaced. */
1344 void installboot(unsigned char *bootblock, char *masterboot)
1345 /* Install code from a master bootstrap into a boot block. */
1347 FILE *mfp;
1348 unsigned char buf[SECTOR_SIZE];
1349 int n;
1350 char *err;
1352 if ((mfp= fopen(masterboot, "r")) == nil) {
1353 err= strerror(errno);
1354 goto m_err;
1357 n= fread(buf, sizeof(char), SECTOR_SIZE, mfp);
1358 if (ferror(mfp)) {
1359 err= strerror(errno);
1360 fclose(mfp);
1361 goto m_err;
1363 else if (n < 256) {
1364 err= "Is probably not a boot sector, too small";
1365 fclose(mfp);
1366 goto m_err;
1368 else if (n < SECTOR_SIZE && n > PART_TABLE_OFF) {
1369 /* if only code, it cannot override partition table */
1370 err= "Does not fit in a boot sector";
1371 fclose(mfp);
1372 goto m_err;
1374 else if (n == SECTOR_SIZE) {
1375 if (buf[510] != 0x55 || buf[511] != 0xaa) {
1376 err= "Is not a boot sector (bad magic)";
1377 fclose(mfp);
1378 goto m_err;
1380 n = PART_TABLE_OFF;
1383 if (n > PART_TABLE_OFF) {
1384 err= "Does not fit in a boot sector";
1385 fclose(mfp);
1386 goto m_err;
1389 memcpy(bootblock, buf, n);
1390 fclose(mfp);
1392 /* Bootstrap installed. */
1393 return;
1395 m_err:
1396 stat_start(1);
1397 printf("%s: %s", masterboot, err);
1398 stat_end(5);
1401 ssize_t boot_readwrite(int rw)
1402 /* Read (0) or write (1) the boot sector. */
1404 u64_t off64 = mul64u(offset, SECTOR_SIZE);
1405 int r = 0;
1407 #if __minix_vmd
1408 /* Minix-vmd has a 64 bit seek. */
1409 if (fcntl(device, F_SEEK, off64) < 0) return -1;
1410 #else
1411 /* Minix has to gross things with the partition base. */
1412 struct partition geom0, geom_seek;
1414 if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) {
1415 /* Move partition base. */
1416 if (ioctl(device, DIOCGETP, &geom0) < 0) return -1;
1417 geom_seek.base = add64(geom0.base, off64);
1418 geom_seek.size = cvu64(cmp64(add64u(off64, SECTOR_SIZE),
1419 geom0.size) <= 0 ? _STATIC_BLOCK_SIZE : 0);
1420 sync();
1421 if (ioctl(device, DIOCSETP, &geom_seek) < 0) return -1;
1422 if (lseek(device, (off_t) 0, SEEK_SET) == -1) return -1;
1423 } else {
1424 /* Can reach this point normally. */
1425 if (lseek(device, (off_t) offset * SECTOR_SIZE, SEEK_SET) == -1)
1426 return -1;
1428 #endif
1430 switch (rw) {
1431 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1432 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1435 #if !__minix_vmd
1436 if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) {
1437 /* Restore partition base and size. */
1438 sync();
1439 if (ioctl(device, DIOCSETP, &geom0) < 0) return -1;
1441 #endif
1442 return r;
1445 int cylinderalign(region_t *reg)
1447 if(reg->is_used_part) {
1448 if(reg->used_part.lowsec != table[0].lowsec + sectors
1449 && (reg->used_part.lowsec % secpcyl)) {
1450 int extra;
1451 extra = secpcyl - (reg->used_part.lowsec % secpcyl);
1452 reg->used_part.lowsec += extra;
1453 reg->used_part.size -= extra;
1455 if((reg->used_part.size+1) % secpcyl) {
1456 reg->used_part.size -= secpcyl - ((reg->used_part.size + 1) % secpcyl);
1458 return reg->used_part.size > 0;
1461 if(reg->free_sec_start != table[0].lowsec + sectors && (reg->free_sec_start % secpcyl)) {
1462 /* Start is unaligned. Round up. */
1463 reg->free_sec_start += secpcyl - (reg->free_sec_start % secpcyl);
1465 if((reg->free_sec_last+1) % secpcyl) {
1466 /* End is unaligned. Round down. */
1467 reg->free_sec_last -= (reg->free_sec_last+1) % secpcyl;
1470 /* Return nonzero if anything remains of the region after rounding. */
1471 return reg->free_sec_last > reg->free_sec_start;
1474 void regionize(void)
1476 int free_sec, i, si;
1478 sort();
1480 free_sec = table[0].lowsec + sectors;
1482 /* Create region data used in autopart mode. */
1483 free_regions = used_regions = nr_regions = nr_partitions = 0;
1484 if(table[0].lowsec > table[sort_order[1]].lowsec &&
1485 table[sort_order[1]].sysind != NO_PART) {
1486 printf("\nSanity check failed on %s - first partition starts before disk.\n"
1487 "Please use expert mode to correct it.\n", curdev->name);
1488 exit(1);
1490 for(si = 1; si <= NR_PARTITIONS; si++) {
1491 i = sort_order[si];
1492 if(i < 1 || i > NR_PARTITIONS) {
1493 printf("Sorry, something unexpected has happened (%d out of range).\n", i);
1494 exit(1);
1497 if(table[i].sysind == NO_PART)
1498 break;
1500 /* Free space before this partition? */
1501 if(table[i].lowsec > free_sec) {
1502 /* Free region before this partition. */
1503 regions[nr_regions].free_sec_start = free_sec;
1504 regions[nr_regions].free_sec_last = table[i].lowsec-1;
1505 regions[nr_regions].is_used_part = 0;
1506 if(cylinderalign(&regions[nr_regions])) {
1507 nr_regions++;
1508 free_regions++;
1512 /* Sanity check. */
1513 if(si > 1) {
1514 if(table[i].lowsec < table[sort_order[si-1]].lowsec ||
1515 table[i].lowsec < table[sort_order[si-1]].lowsec + table[sort_order[si-1]].size) {
1516 printf("\nSanity check failed on %s - partitions overlap.\n"
1517 "Please use expert mode to correct it.\n", curdev->name);
1518 exit(1);
1521 if(table[i].size > table[0].size) {
1522 printf("\nSanity check failed on %s - partition is larger than disk.\n"
1523 "Please use expert mode to correct it.\n", curdev->name);
1524 exit(1);
1526 if(table[i].size < 1) {
1527 printf("\nSanity check failed on %s - zero-sized partition.\n"
1528 "Please use expert mode to correct it.\n", curdev->name);
1529 exit(1);
1532 /* Remember used region. */
1533 memcpy(&regions[nr_regions].used_part, &table[i], sizeof(table[i]));
1534 free_sec = table[i].lowsec+table[i].size;
1535 regions[nr_regions].is_used_part = 1;
1536 regions[nr_regions].tableno = i;
1537 nr_partitions++;
1538 nr_regions++;
1539 used_regions++;
1542 /* Special case: space after partitions. */
1543 if(free_sec < table[0].lowsec + table[0].size-1) {
1544 regions[nr_regions].free_sec_start = free_sec;
1545 regions[nr_regions].free_sec_last = table[0].lowsec + table[0].size-1;
1546 regions[nr_regions].is_used_part = 0;
1547 if(cylinderalign(&regions[nr_regions])) {
1548 nr_regions++;
1549 free_regions++;
1555 void m_read(int ev, int *biosdrive)
1556 /* Read the partition table from the current device. */
1558 int i, mode, n, v;
1559 struct part_entry *pe;
1560 u32_t system_hz;
1562 if (ev != 'r' || device >= 0) return;
1564 /* Open() may cause kernel messages: */
1565 stat_start(0);
1566 fflush(stdout);
1568 if ((device= open(curdev->name, mode= O_RDWR, 0666)) < 0) {
1569 if (device >= 0) { close(device); device= -1; }
1570 return;
1573 system_hz = (u32_t) sysconf(_SC_CLK_TCK);
1574 v = 2*system_hz;
1575 ioctl(device, DIOCTIMEOUT, &v);
1577 memset(bootblock, 0, sizeof(bootblock));
1579 n= boot_readwrite(0);
1581 if (n <= 0) stat_start(1);
1582 if (n < 0) {
1583 close(device);
1584 device= -1;
1585 } else
1586 if (n < SECTOR_SIZE) {
1587 close(device);
1588 device= -1;
1589 return;
1591 if (n <= 0) stat_end(5);
1593 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1595 if(biosdrive) (*biosdrive)++;
1597 if(!open_ct_ok(device)) {
1598 printf("\n%s: device in use! skipping it.", curdev->subname);
1599 fflush(stdout);
1600 close(device);
1601 device= -1;
1602 return;
1605 memcpy(table+1, bootblock+PART_TABLE_OFF,
1606 NR_PARTITIONS * sizeof(table[1]));
1607 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1608 /* Invalid boot block, install bootstrap, wipe partition table.
1610 memset(bootblock, 0, sizeof(bootblock));
1611 installboot(bootblock, MASTERBOOT);
1612 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1615 /* Fix an extended partition table up to something mere mortals can
1616 * understand. Record already defined partitions.
1618 for (i= 1; i <= NR_PARTITIONS; i++) {
1619 pe= &table[i];
1620 if (extbase != 0 && pe->sysind != NO_PART)
1621 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1622 existing[i]= pe->sysind != NO_PART;
1624 geometry();
1625 dirty= 0;
1627 /* Warn about grave dangers ahead. */
1628 if (extbase != 0) {
1629 stat_start(1);
1630 printf("Warning: You are in an extended partition.");
1631 stat_end(5);
1634 regionize();
1637 void m_write(int ev, object_t *op)
1638 /* Write the partition table back if modified. */
1640 struct part_entry new_table[NR_PARTITIONS], *pe;
1642 if (ev != 'w' && ev != E_WRITE) return;
1643 if (device < 0) { dirty= 0; return; }
1644 if (!dirty) {
1645 if (ev == 'w') {
1646 stat_start(1);
1647 printf("%s is not changed, or has already been written",
1648 curdev->subname);
1649 stat_end(2);
1651 return;
1654 if (extbase != 0) {
1655 /* Will this stop him? Probably not... */
1656 stat_start(1);
1657 printf("You have changed an extended partition. Bad Idea.");
1658 stat_end(5);
1661 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1662 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1663 if (pe->sysind == NO_PART) {
1664 memset(pe, 0, sizeof(*pe));
1665 } else {
1666 abs2dos(&pe->start_head, pe->lowsec);
1667 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1669 /* Fear and loathing time: */
1670 if (extbase != 0)
1671 pe->lowsec-= ext_part(pe->sysind)
1672 ? extbase : offset;
1675 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1676 bootblock[510]= 0x55;
1677 bootblock[511]= 0xAA;
1679 if (boot_readwrite(1) < 0) {
1680 stat_start(1);
1681 printf("%s: %s", curdev->name, strerror(errno));
1682 stat_end(5);
1683 return;
1685 dirty= 0;
1688 void m_shell(int ev, object_t *op)
1689 /* Shell escape, to do calculations for instance. */
1691 int r, pid, status;
1692 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1694 if (ev != 's') return;
1696 reset_tty();
1697 fflush(stdout);
1699 switch (pid= fork()) {
1700 case -1:
1701 stat_start(1);
1702 printf("can't fork: %s\n", strerror(errno));
1703 stat_end(3);
1704 break;
1705 case 0:
1706 if (device >= 0) (void) close(device);
1707 execl("/bin/sh", "sh", (char *) nil);
1708 r= errno;
1709 stat_start(1);
1710 printf("/bin/sh: %s\n", strerror(errno));
1711 stat_end(3);
1712 exit(127);
1714 sigint= signal(SIGINT, SIG_IGN);
1715 sigquit= signal(SIGQUIT, SIG_IGN);
1716 sigterm= signal(SIGTERM, SIG_IGN);
1717 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1718 (void) signal(SIGINT, sigint);
1719 (void) signal(SIGQUIT, sigquit);
1720 (void) signal(SIGTERM, sigterm);
1721 tty_raw();
1722 if (pid < 0)
1724 else
1725 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1726 stat_start(0); /* Match the stat_start in the child. */
1727 else
1728 event(ctrl('L'), op);
1731 int quitting= 0;
1733 void m_quit(int ev, object_t *op)
1734 /* Write the partition table if modified and exit. */
1736 if (ev != 'q' && ev != 'x') return;
1738 quitting= 1;
1740 if (dirty) event(E_WRITE, op);
1741 if (dirty) quitting= 0;
1744 void m_help(int ev, object_t *op)
1745 /* For people without a clue; let's hope they can find the '?' key. */
1747 static struct help {
1748 char *keys;
1749 char *what;
1750 } help[]= {
1751 { "? !", "This help / more advice!" },
1752 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1753 { "0-9 (a-f)", "Enter value" },
1754 { "hjkl (arrow keys)", "Move around" },
1755 { "CTRL-K CTRL-J", "Move entry up/down" },
1756 { "CTRL-L", "Redraw screen" },
1757 { ">", "Start a subpartition table" },
1758 { "<", "Back to the primary partition table" },
1759 { "m", "Cycle through magic values" },
1760 { "spacebar", "Show \"Size\" or \"Last\"" },
1761 { "r w", "Read/write partition table" },
1762 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1763 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1765 static char *advice[] = {
1766 "* Choose a disk with '+' and '-', then hit 'r'.",
1767 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1768 "* To make a new partition: Move over to the Size or Kb field of an unused",
1769 " partition and type the size. Hit the 'm' key to pad the partition out to",
1770 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1771 " You can hit 'm' more than once on a base or size field to see several",
1772 " interesting values go by. Note: Other Operating Systems can be picky about",
1773 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1774 " head or sector numbers.",
1775 "* To reuse a partition: Change the type to MINIX.",
1776 "* To delete a partition: Type a zero in the hex Type field.",
1777 "* To make a partition active: Type '+' in the Num field.",
1778 "* To study the list of keys: Type '?'.",
1781 if (ev == '?') {
1782 struct help *hp;
1784 for (hp= help; hp < arraylimit(help); hp++) {
1785 stat_start(0);
1786 printf("%-25s - %s", hp->keys, hp->what);
1787 stat_end(0);
1789 stat_start(0);
1790 putstr("Things like ");
1791 putstr(t_so); putstr("this"); putstr(t_se);
1792 putstr(" must be checked, but ");
1793 putstr(t_md); putstr("this"); putstr(t_me);
1794 putstr(" is not really a problem");
1795 stat_end(0);
1796 } else
1797 if (ev == '!') {
1798 char **ap;
1800 for (ap= advice; ap < arraylimit(advice); ap++) {
1801 stat_start(0);
1802 putstr(*ap);
1803 stat_end(0);
1808 void event(int ev, object_t *op)
1809 /* Simply call all modifiers for an event, each one knows when to act. */
1811 m_help(ev, op);
1812 m_redraw(ev, op);
1813 m_toggle(ev, op);
1814 m_orientation(ev, op);
1815 m_move(ev, op);
1816 m_updown(ev, op);
1817 m_enter(ev, op);
1818 m_leave(ev, op);
1819 m_modify(ev, op);
1820 m_magic(ev, op);
1821 m_in(ev, op);
1822 m_out(ev, op);
1823 m_read(ev, NULL);
1824 m_write(ev, op);
1825 m_shell(ev, op);
1826 m_quit(ev, op);
1829 char *
1830 prettysizeprint(int kb)
1832 int toosmall = 0;
1833 static char str[200];
1834 char unit = 'k';
1835 if(MIN_REGION_SECTORS > kb*2)
1836 toosmall = 1;
1837 if(kb >= 5*1024) {
1838 kb /= 1024;
1839 unit = 'M';
1840 if(kb >= 5*1024) {
1841 kb /= 1024;
1842 unit = 'G';
1845 sprintf(str, "%4d %cB%s", kb, unit,
1846 toosmall ? ", too small for MINIX 3" : "");
1847 return str;
1850 void
1851 printregions(region_t *theregions, int indent, int p_nr_partitions, int p_free_regions, int p_nr_regions, int numbers)
1853 int r, nofree = 0;
1854 region_t *reg;
1855 reg = theregions;
1857 if((p_nr_partitions >= NR_PARTITIONS || !p_free_regions) && p_free_regions)
1858 nofree = 1;
1859 for(r = 0; r < p_nr_regions; r++, reg++) {
1860 unsigned long units;
1861 if(reg->is_used_part) {
1862 char *name;
1863 name = typ2txt(reg->used_part.sysind);
1864 printf("%*s", indent, ""); type2col(reg->used_part.sysind);
1865 if(numbers) printf("[%d] ", r);
1866 printf("In use by %-10s ", name);
1867 units = reg->used_part.size / 2;
1868 col(0);
1869 printf(" (%s)\n", prettysizeprint(units));
1870 } else {
1871 printf("%*s", indent, "");
1872 if(numbers) {
1873 if(!nofree) printf("[%d] ", r);
1874 else printf("[-] ");
1876 printf("Free space ");
1877 units = ((reg->free_sec_last - reg->free_sec_start+1))/2;
1878 printf(" (%s)\n", prettysizeprint(units));
1882 if(numbers && p_nr_partitions >= NR_PARTITIONS && p_free_regions) {
1883 printf(
1884 "\nNote: there is free space on this disk, but you can't select it,\n"
1885 "because there isn't a free slot in the partition table to use it.\n"
1886 "You can reclaim the free space by deleting an adjacent region.\n");
1889 return;
1892 #define IS_YES 3
1893 #define IS_NO 4
1894 #define IS_OTHER 5
1896 is_sure(char *fmt, ...)
1898 char yesno[10];
1899 va_list ap;
1900 va_start (ap, fmt);
1901 vprintf(fmt, ap);
1902 va_end(ap);
1903 printf(" Please enter 'yes' or 'no': ");
1904 fflush(stdout);
1905 if(!fgets(yesno, sizeof(yesno)-1, stdin)) exit(1);
1907 if (strcmp(yesno, "yes\n") == 0) return(IS_YES);
1908 if (strcmp(yesno, "no\n") == 0) return(IS_NO);
1909 return IS_OTHER;
1912 void warn(char *message)
1914 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);
1918 may_kill_region(void)
1920 int confirmation;
1921 char line[100];
1922 int r, i;
1924 if(used_regions < 1) return 1;
1926 printf("\n -- Delete in-use region? --\n\n");
1928 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1929 printf("\nEnter the region number to delete or ENTER to continue: ");
1930 fflush(NULL);
1931 fgets(line, sizeof(line)-2, stdin);
1932 if(!isdigit(line[0]))
1933 return 1;
1935 r=atoi(line);
1936 if(r < 0 || r >= nr_regions) {
1937 printf("This choice is out of range.\n");
1938 return 0;
1941 if(!regions[r].is_used_part) {
1942 printf("This region is not in use.\n");
1943 return 0;
1946 i = regions[r].tableno;
1948 printf("\nPlease confirm that you want to delete region %d, losing all data it", r);
1949 printf("\ncontains. You're disk is not actually updated right away, but still.");
1950 printf("\n\n");
1952 do {
1953 confirmation = is_sure("Are you sure you want to continue?");
1954 if (confirmation == IS_NO) return 0;
1955 } while (confirmation != IS_YES);
1957 table[i].sysind = NO_PART;
1958 dirty = 1;
1959 regionize();
1961 /* User may go again. */
1962 return 0;
1966 region_t *
1967 select_region(void)
1969 int rn, done = 0;
1970 static char line[100];
1971 int nofree = 0;
1973 printstep(2, "Select a disk region");
1975 if(nr_regions < 1) {
1976 printf("\nNo regions found - maybe the drive is too small.\n"
1977 "Please try expert mode.\n");
1978 exit(1);
1981 if(nr_partitions >= NR_PARTITIONS || !free_regions) {
1982 if(free_regions) {
1983 nofree = 1;
1988 printf("\nPlease select the region that you want to use for the MINIX 3 setup.");
1989 printf("\nIf you select an in-use region it will be overwritten by MINIX. The");
1990 printf("\nfollowing region%s were found on the selected disk:\n\n",
1991 SORNOT(nr_regions));
1992 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1995 printf("\n");
1996 do {
1997 printf("Enter the region number to use or type 'delete': ");
1998 if(nr_regions == 1) printf(" [0] ");
1999 fflush(NULL);
2001 if(!fgets(line, sizeof(line)-2, stdin))
2002 exit(1);
2004 if (nr_regions == 1 && line[0] == '\n') {
2005 rn = 0;
2006 done = 1;
2008 else {
2009 if(strcmp(line,"delete\n") == 0) {
2010 may_kill_region();
2011 return NULL;
2014 if(sscanf(line, "%d", &rn) != 1) {
2015 warn("invalid choice");
2016 continue;
2019 if(rn < 0 || rn >= nr_regions) {
2020 warn("out of range");
2021 continue;
2024 if(nofree && !regions[rn].is_used_part) {
2025 warn("not available");
2026 continue;
2029 done = 1;
2031 } while(! done);
2033 return(&regions[rn]);
2036 void printstep(int step, char *str)
2038 int n;
2039 n = printf("\n --- Substep 3.%d: %s ---", step, str);
2040 while(n++ < 73) printf("-");
2041 printf("\n");
2044 device_t *
2045 select_disk(void)
2047 int done = 0;
2048 int i, choice, drives;
2049 static char line[500];
2050 int biosdrive = 0;
2052 printstep(1, "Select a disk to install MINIX 3");
2053 printf("\nProbing for disks. This may take a short while.");
2055 i = 0;
2056 curdev=firstdev;
2058 for(; i < MAX_DEVICES;) {
2059 printf(".");
2060 fflush(stdout);
2061 m_read('r', &biosdrive);
2062 if(device >= 0) {
2063 devices[i].dev = curdev;
2064 devices[i].free_regions = free_regions;
2065 devices[i].nr_regions = nr_regions;
2066 devices[i].nr_partitions = nr_partitions;
2067 devices[i].used_regions = used_regions;
2068 devices[i].sectors = table[0].size;
2069 curdev->biosdrive = biosdrive-1;
2070 memcpy(devices[i].regions, regions, sizeof(regions));
2071 i++;
2074 nextdevice(NULL, 1);
2075 if(curdev == firstdev)
2076 break;
2079 drives = i;
2081 if(drives < 1) {
2082 printf("\nFound no drives - can't partition.\n");
2083 exit(1);
2086 printf(" Probing done.\n");
2087 printf("The following disk%s %s found on your system:\n\n", SORNOT(drives),
2088 drives == 1 ? "was" : "were");
2090 for(i = 0; i < drives; i++) {
2091 printf(" ");
2092 printf("Disk [%d]: ", i);
2093 printf("%s, ", devices[i].dev->name);
2094 printf("%s\n", prettysizeprint(devices[i].sectors/2));
2095 printregions(devices[i].regions, 8,
2096 devices[i].nr_partitions,
2097 devices[i].free_regions,
2098 devices[i].nr_regions, 0);
2101 printf("\n");
2102 do {
2103 printf("Enter the disk number to use: ");
2104 if (drives == 1) printf("[0] ");
2105 fflush(NULL);
2106 if(!fgets(line, sizeof(line)-2, stdin))
2107 exit(1);
2108 if (line[0] == '\n' && drives == 1) {
2109 choice = 0;
2110 done = 1;
2111 } else {
2112 if(sscanf(line, "%d", &choice) != 1) {
2113 warn("choose a disk");
2114 continue;
2116 if(choice < 0 || choice >= i) {
2117 warn("out of range");
2118 continue;
2120 done = 1;
2122 } while(! done);
2123 return devices[choice].dev;
2127 scribble_region(region_t *reg, struct part_entry **pe, int *made_new)
2129 int ex, changed = 0, i;
2130 struct part_entry *newpart;
2131 if(!reg->is_used_part) {
2132 ex = reg->free_sec_last - reg->free_sec_start + 1;
2133 if(made_new) *made_new = 1;
2134 } else if(made_new) *made_new = 0;
2135 if(!reg->is_used_part) {
2136 for(i = 1; i <= NR_PARTITIONS; i++)
2137 if(table[i].sysind == NO_PART)
2138 break;
2139 if(i > NR_PARTITIONS) {
2140 /* Bug, should've been caught earlier. */
2141 printf("Couldn't find a free slot. Please try expert mode.\n");
2142 exit(1);
2144 newpart = &table[i];
2145 newpart->lowsec = reg->free_sec_start;
2146 newpart->size = reg->free_sec_last - reg->free_sec_start + 1;
2147 changed = 1;
2148 newpart->sysind = MINIX_PART;
2149 } else {
2150 newpart = &reg->used_part;
2152 *pe = newpart;
2153 changed = 1;
2154 dirty = 1;
2155 return changed;
2159 sanitycheck_failed(char *dev, struct part_entry *pe)
2161 struct partition part;
2162 int fd;
2163 unsigned long it_lowsec, it_secsize;
2165 if((fd = open(dev, O_RDONLY)) < 0) {
2166 perror(dev);
2167 return 1;
2170 if (ioctl(fd, DIOCGETP, &part) < 0) {
2171 fprintf(stderr, "DIOCGETP failed\n");
2172 perror(dev);
2173 return 1;
2176 if(!open_ct_ok(fd)) {
2177 printf("\nAutopart error: the disk is in use. This means that although a\n"
2178 "new table has been written, it won't be in use by the system\n"
2179 "until it's no longer in use (or a reboot is done). Just in case,\n"
2180 "I'm not going to continue. Please un-use the disk (or reboot) and try\n"
2181 "again.\n\n");
2182 return 1;
2185 close(fd);
2187 it_lowsec = div64u(part.base, SECTOR_SIZE);
2188 it_secsize = div64u(part.size, SECTOR_SIZE);
2190 if(it_lowsec != pe->lowsec || it_secsize != pe->size) {
2191 fprintf(stderr, "\nReturned and set numbers don't match up!\n");
2192 fprintf(stderr, "This can happen if the disk is still opened.\n");
2193 return 1;
2196 return 0;
2200 do_autopart(int resultfd)
2202 int confirmation;
2203 region_t *r;
2204 struct part_entry *pe;
2205 struct part_entry orig_table[1 + NR_PARTITIONS];
2206 int region, newp;
2208 nordonly = 1;
2210 do {
2211 curdev = select_disk();
2212 } while(!curdev);
2214 if(device >= 0) {
2215 close(device);
2216 device = -1;
2218 recompute0();
2220 m_read('r', NULL);
2222 memcpy(orig_table, table, sizeof(table));
2224 do {
2225 /* Show regions. */
2226 r = select_region();
2227 } while(!r); /* Back to step 2. */
2229 /* Write things. */
2230 if(scribble_region(r, &pe, &newp)) {
2231 char *name;
2232 int i, found = -1;
2233 char partbuf[100], devname[100];
2234 struct part_entry *tpe = NULL;
2236 printstep(3, "Confirm your choices");
2238 region = (int)(r-regions);
2239 /* disk = (int) (curdev-devices); */
2241 printf("\nThis is the point of no return. You have selected to install MINIX 3\n");
2242 printf("into region %d of disk %s. Please confirm that you want\n",
2243 region, curdev->name);
2244 printf("to use this selection to install MINIX 3.\n\n");
2246 do {
2247 confirmation = is_sure("Are you sure you want to continue?");
2248 if (confirmation == IS_NO) return 1;
2249 } while (confirmation != IS_YES);
2251 /* Retrieve partition number in sorted order that we
2252 * have scribbled in.
2254 sort();
2255 for(i = 1; i <= NR_PARTITIONS; i++) {
2256 int si;
2257 si = sort_order[i];
2258 if(si < 1 || si > NR_PARTITIONS) {
2259 fprintf(stderr, "Autopart internal error (out of range) (nothing written).\n");
2260 exit(1);
2262 if(table[si].lowsec == pe->lowsec) {
2263 if(found > 0) {
2264 fprintf(stderr, "Autopart internal error (part found twice) (nothing written).\n");
2265 exit(1);
2267 check_ind(&table[si]);
2268 table[si].sysind = MINIX_PART;
2269 found = i;
2270 tpe = &table[si];
2273 if(found < 1) {
2274 fprintf(stderr, "Autopart internal error (part not found) (nothing written).\n");
2275 exit(1);
2277 m_write('w', NULL);
2278 if(dirty) {
2279 fprintf(stderr, "Autopart internal error (couldn't update disk).\n");
2280 exit(1);
2282 name=strrchr(curdev->name, '/');
2283 if(!name) name = curdev->name;
2284 else name++;
2286 sprintf(partbuf, "%sp%d d%dp%d\n", name, found-1,
2287 curdev->biosdrive, found-1);
2288 sprintf(devname, "/dev/%sp%d", name, found-1);
2289 if(resultfd >= 0 && write(resultfd, partbuf, strlen(partbuf)) < strlen(partbuf)) {
2290 fprintf(stderr, "Autopart internal error (couldn't write result).\n");
2291 exit(1);
2293 if(device >= 0) {
2294 close(device);
2295 device = -1;
2298 #if 0
2299 m_dump(orig_table);
2300 printf("\n");
2301 m_dump(table);
2302 #endif
2304 if(sanitycheck_failed(devname, tpe)) {
2305 fprintf(stderr, "Autopart internal error (disk sanity check failed).\n");
2306 exit(1);
2309 if(newp) {
2310 int fd;
2311 if((fd=open(devname, O_WRONLY)) < 0) {
2312 perror(devname);
2313 } else {
2314 /* Clear any subpartitioning. */
2315 static unsigned char sub[2048];
2316 sub[510] = 0x55;
2317 sub[511] = 0xAA;
2318 write(fd, sub, sizeof(sub));
2319 close(fd);
2322 return 0;
2325 return 1;
2328 int main(int argc, char **argv)
2330 int c;
2331 int i, key;
2332 int resultfd = -1;
2334 /* autopart uses getopt() */
2335 while((c = getopt(argc, argv, "m:f:")) != EOF) {
2336 switch(c) {
2337 case 'm':
2338 min_region_mb = atoi(optarg);
2339 break;
2340 case 'f':
2341 /* Make sure old data file is gone. */
2342 unlink(optarg);
2343 if((resultfd=open(optarg, O_CREAT | O_WRONLY | O_TRUNC)) < 0) {
2344 perror(optarg);
2345 return 1;
2347 sync(); /* Make sure no old data file lingers. */
2348 break;
2349 default:
2350 fprintf(stderr, "Unknown option\n");
2351 return 1;
2355 argc -= optind;
2356 argv += optind;
2358 for (i= 0; i < argc; i++) {
2359 newdevice(argv[i], 0, 0);
2362 if (firstdev == nil) {
2363 getdevices();
2364 key= ctrl('L');
2365 } else {
2366 key= 'r';
2370 int r;
2371 if (firstdev == nil) {
2372 fprintf(stderr, "autopart couldn't find any devices.\n");
2373 return 1;
2375 r = do_autopart(resultfd);
2376 if(resultfd >= 0) { close(resultfd); }
2377 return r;
2380 exit(0);