iso9660fs: initialize buffer cache
[minix.git] / commands / autopart / autopart.c
blobe07022c51f6d5922fe19fdedbcf32419d7d06290
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 #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 int 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 int r = 0;
1406 if (lseek64(device, (u64_t) offset * SECTOR_SIZE, SEEK_SET, NULL) < 0)
1407 return -1;
1409 switch (rw) {
1410 case 0: r= read(device, bootblock, SECTOR_SIZE); break;
1411 case 1: r= write(device, bootblock, SECTOR_SIZE); break;
1414 return r;
1417 int cylinderalign(region_t *reg)
1419 if(reg->is_used_part) {
1420 if(reg->used_part.lowsec != table[0].lowsec + sectors
1421 && (reg->used_part.lowsec % secpcyl)) {
1422 int extra;
1423 extra = secpcyl - (reg->used_part.lowsec % secpcyl);
1424 reg->used_part.lowsec += extra;
1425 reg->used_part.size -= extra;
1427 if((reg->used_part.size+1) % secpcyl) {
1428 reg->used_part.size -= secpcyl - ((reg->used_part.size + 1) % secpcyl);
1430 return reg->used_part.size > 0;
1433 if(reg->free_sec_start != table[0].lowsec + sectors && (reg->free_sec_start % secpcyl)) {
1434 /* Start is unaligned. Round up. */
1435 reg->free_sec_start += secpcyl - (reg->free_sec_start % secpcyl);
1437 if((reg->free_sec_last+1) % secpcyl) {
1438 /* End is unaligned. Round down. */
1439 reg->free_sec_last -= (reg->free_sec_last+1) % secpcyl;
1442 /* Return nonzero if anything remains of the region after rounding. */
1443 return reg->free_sec_last > reg->free_sec_start;
1446 void regionize(void)
1448 int free_sec, i, si;
1450 sort();
1452 free_sec = table[0].lowsec + sectors;
1454 /* Create region data used in autopart mode. */
1455 free_regions = used_regions = nr_regions = nr_partitions = 0;
1456 if(table[0].lowsec > table[sort_order[1]].lowsec &&
1457 table[sort_order[1]].sysind != NO_PART) {
1458 printf("\nSanity check failed on %s - first partition starts before disk.\n"
1459 "Please use expert mode to correct it.\n", curdev->name);
1460 exit(1);
1462 for(si = 1; si <= NR_PARTITIONS; si++) {
1463 i = sort_order[si];
1464 if(i < 1 || i > NR_PARTITIONS) {
1465 printf("Sorry, something unexpected has happened (%d out of range).\n", i);
1466 exit(1);
1469 if(table[i].sysind == NO_PART)
1470 break;
1472 /* Free space before this partition? */
1473 if(table[i].lowsec > free_sec) {
1474 /* Free region before this partition. */
1475 regions[nr_regions].free_sec_start = free_sec;
1476 regions[nr_regions].free_sec_last = table[i].lowsec-1;
1477 regions[nr_regions].is_used_part = 0;
1478 if(cylinderalign(&regions[nr_regions])) {
1479 nr_regions++;
1480 free_regions++;
1484 /* Sanity check. */
1485 if(si > 1) {
1486 if(table[i].lowsec < table[sort_order[si-1]].lowsec ||
1487 table[i].lowsec < table[sort_order[si-1]].lowsec + table[sort_order[si-1]].size) {
1488 printf("\nSanity check failed on %s - partitions overlap.\n"
1489 "Please use expert mode to correct it.\n", curdev->name);
1490 exit(1);
1493 if(table[i].size > table[0].size) {
1494 printf("\nSanity check failed on %s - partition is larger than disk.\n"
1495 "Please use expert mode to correct it.\n", curdev->name);
1496 exit(1);
1498 if(table[i].size < 1) {
1499 printf("\nSanity check failed on %s - zero-sized partition.\n"
1500 "Please use expert mode to correct it.\n", curdev->name);
1501 exit(1);
1504 /* Remember used region. */
1505 memcpy(&regions[nr_regions].used_part, &table[i], sizeof(table[i]));
1506 free_sec = table[i].lowsec+table[i].size;
1507 regions[nr_regions].is_used_part = 1;
1508 regions[nr_regions].tableno = i;
1509 nr_partitions++;
1510 nr_regions++;
1511 used_regions++;
1514 /* Special case: space after partitions. */
1515 if(free_sec < table[0].lowsec + table[0].size-1) {
1516 regions[nr_regions].free_sec_start = free_sec;
1517 regions[nr_regions].free_sec_last = table[0].lowsec + table[0].size-1;
1518 regions[nr_regions].is_used_part = 0;
1519 if(cylinderalign(&regions[nr_regions])) {
1520 nr_regions++;
1521 free_regions++;
1527 void m_read(int ev, int *biosdrive)
1528 /* Read the partition table from the current device. */
1530 int i, mode, n, v;
1531 struct part_entry *pe;
1532 u32_t system_hz;
1534 if (ev != 'r' || device >= 0) return;
1536 /* Open() may cause kernel messages: */
1537 stat_start(0);
1538 fflush(stdout);
1540 if ((device= open(curdev->name, mode= O_RDWR, 0666)) < 0) {
1541 if (device >= 0) { close(device); device= -1; }
1542 return;
1545 system_hz = (u32_t) sysconf(_SC_CLK_TCK);
1546 v = 2*system_hz;
1547 ioctl(device, DIOCTIMEOUT, &v);
1549 memset(bootblock, 0, sizeof(bootblock));
1551 n= boot_readwrite(0);
1553 if (n <= 0) stat_start(1);
1554 if (n < 0) {
1555 close(device);
1556 device= -1;
1557 } else
1558 if (n < SECTOR_SIZE) {
1559 close(device);
1560 device= -1;
1561 return;
1563 if (n <= 0) stat_end(5);
1565 if (n < SECTOR_SIZE) n= SECTOR_SIZE;
1567 if(biosdrive) (*biosdrive)++;
1569 if(!open_ct_ok(device)) {
1570 printf("\n%s: device in use! skipping it.", curdev->subname);
1571 fflush(stdout);
1572 close(device);
1573 device= -1;
1574 return;
1577 memcpy(table+1, bootblock+PART_TABLE_OFF,
1578 NR_PARTITIONS * sizeof(table[1]));
1579 if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) {
1580 /* Invalid boot block, install bootstrap, wipe partition table.
1582 memset(bootblock, 0, sizeof(bootblock));
1583 installboot(bootblock, MASTERBOOT);
1584 memset(table+1, 0, NR_PARTITIONS * sizeof(table[1]));
1587 /* Fix an extended partition table up to something mere mortals can
1588 * understand. Record already defined partitions.
1590 for (i= 1; i <= NR_PARTITIONS; i++) {
1591 pe= &table[i];
1592 if (extbase != 0 && pe->sysind != NO_PART)
1593 pe->lowsec+= ext_part(pe->sysind) ? extbase : offset;
1594 existing[i]= pe->sysind != NO_PART;
1596 geometry();
1597 dirty= 0;
1599 /* Warn about grave dangers ahead. */
1600 if (extbase != 0) {
1601 stat_start(1);
1602 printf("Warning: You are in an extended partition.");
1603 stat_end(5);
1606 regionize();
1609 void m_write(int ev, object_t *op)
1610 /* Write the partition table back if modified. */
1612 struct part_entry new_table[NR_PARTITIONS], *pe;
1614 if (ev != 'w' && ev != E_WRITE) return;
1615 if (device < 0) { dirty= 0; return; }
1616 if (!dirty) {
1617 if (ev == 'w') {
1618 stat_start(1);
1619 printf("%s is not changed, or has already been written",
1620 curdev->subname);
1621 stat_end(2);
1623 return;
1626 if (extbase != 0) {
1627 /* Will this stop him? Probably not... */
1628 stat_start(1);
1629 printf("You have changed an extended partition. Bad Idea.");
1630 stat_end(5);
1633 memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1]));
1634 for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) {
1635 if (pe->sysind == NO_PART) {
1636 memset(pe, 0, sizeof(*pe));
1637 } else {
1638 abs2dos(&pe->start_head, pe->lowsec);
1639 abs2dos(&pe->last_head, pe->lowsec + pe->size - 1);
1641 /* Fear and loathing time: */
1642 if (extbase != 0)
1643 pe->lowsec-= ext_part(pe->sysind)
1644 ? extbase : offset;
1647 memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table));
1648 bootblock[510]= 0x55;
1649 bootblock[511]= 0xAA;
1651 if (boot_readwrite(1) < 0) {
1652 stat_start(1);
1653 printf("%s: %s", curdev->name, strerror(errno));
1654 stat_end(5);
1655 return;
1657 dirty= 0;
1660 void m_shell(int ev, object_t *op)
1661 /* Shell escape, to do calculations for instance. */
1663 int r, pid, status;
1664 void (*sigint)(int), (*sigquit)(int), (*sigterm)(int);
1666 if (ev != 's') return;
1668 reset_tty();
1669 fflush(stdout);
1671 switch (pid= fork()) {
1672 case -1:
1673 stat_start(1);
1674 printf("can't fork: %s\n", strerror(errno));
1675 stat_end(3);
1676 break;
1677 case 0:
1678 if (device >= 0) (void) close(device);
1679 execl("/bin/sh", "sh", (char *) nil);
1680 r= errno;
1681 stat_start(1);
1682 printf("/bin/sh: %s\n", strerror(errno));
1683 stat_end(3);
1684 exit(127);
1686 sigint= signal(SIGINT, SIG_IGN);
1687 sigquit= signal(SIGQUIT, SIG_IGN);
1688 sigterm= signal(SIGTERM, SIG_IGN);
1689 while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {}
1690 (void) signal(SIGINT, sigint);
1691 (void) signal(SIGQUIT, sigquit);
1692 (void) signal(SIGTERM, sigterm);
1693 tty_raw();
1694 if (pid < 0)
1696 else
1697 if (WIFEXITED(status) && WEXITSTATUS(status) == 127)
1698 stat_start(0); /* Match the stat_start in the child. */
1699 else
1700 event(ctrl('L'), op);
1703 int quitting= 0;
1705 void m_quit(int ev, object_t *op)
1706 /* Write the partition table if modified and exit. */
1708 if (ev != 'q' && ev != 'x') return;
1710 quitting= 1;
1712 if (dirty) event(E_WRITE, op);
1713 if (dirty) quitting= 0;
1716 void m_help(int ev, object_t *op)
1717 /* For people without a clue; let's hope they can find the '?' key. */
1719 static struct help {
1720 char *keys;
1721 char *what;
1722 } help[]= {
1723 { "? !", "This help / more advice!" },
1724 { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" },
1725 { "0-9 (a-f)", "Enter value" },
1726 { "hjkl (arrow keys)", "Move around" },
1727 { "CTRL-K CTRL-J", "Move entry up/down" },
1728 { "CTRL-L", "Redraw screen" },
1729 { ">", "Start a subpartition table" },
1730 { "<", "Back to the primary partition table" },
1731 { "m", "Cycle through magic values" },
1732 { "spacebar", "Show \"Size\" or \"Last\"" },
1733 { "r w", "Read/write partition table" },
1734 { "p s q x", "Raw dump / Shell escape / Quit / Exit" },
1735 { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" },
1737 static char *advice[] = {
1738 "* Choose a disk with '+' and '-', then hit 'r'.",
1739 "* To change any value: Move to it and use '+', '-' or type the desired value.",
1740 "* To make a new partition: Move over to the Size or Kb field of an unused",
1741 " partition and type the size. Hit the 'm' key to pad the partition out to",
1742 " a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.",
1743 " You can hit 'm' more than once on a base or size field to see several",
1744 " interesting values go by. Note: Other Operating Systems can be picky about",
1745 " partitions that are not padded to cylinder boundaries. Look for highlighted",
1746 " head or sector numbers.",
1747 "* To reuse a partition: Change the type to MINIX.",
1748 "* To delete a partition: Type a zero in the hex Type field.",
1749 "* To make a partition active: Type '+' in the Num field.",
1750 "* To study the list of keys: Type '?'.",
1753 if (ev == '?') {
1754 struct help *hp;
1756 for (hp= help; hp < arraylimit(help); hp++) {
1757 stat_start(0);
1758 printf("%-25s - %s", hp->keys, hp->what);
1759 stat_end(0);
1761 stat_start(0);
1762 putstr("Things like ");
1763 putstr(t_so); putstr("this"); putstr(t_se);
1764 putstr(" must be checked, but ");
1765 putstr(t_md); putstr("this"); putstr(t_me);
1766 putstr(" is not really a problem");
1767 stat_end(0);
1768 } else
1769 if (ev == '!') {
1770 char **ap;
1772 for (ap= advice; ap < arraylimit(advice); ap++) {
1773 stat_start(0);
1774 putstr(*ap);
1775 stat_end(0);
1780 void event(int ev, object_t *op)
1781 /* Simply call all modifiers for an event, each one knows when to act. */
1783 m_help(ev, op);
1784 m_redraw(ev, op);
1785 m_toggle(ev, op);
1786 m_orientation(ev, op);
1787 m_move(ev, op);
1788 m_updown(ev, op);
1789 m_enter(ev, op);
1790 m_leave(ev, op);
1791 m_modify(ev, op);
1792 m_magic(ev, op);
1793 m_in(ev, op);
1794 m_out(ev, op);
1795 m_read(ev, NULL);
1796 m_write(ev, op);
1797 m_shell(ev, op);
1798 m_quit(ev, op);
1801 char *
1802 prettysizeprint(int kb)
1804 int toosmall = 0;
1805 static char str[200];
1806 char unit = 'k';
1807 if(MIN_REGION_SECTORS > kb*2)
1808 toosmall = 1;
1809 if(kb >= 5*1024) {
1810 kb /= 1024;
1811 unit = 'M';
1812 if(kb >= 5*1024) {
1813 kb /= 1024;
1814 unit = 'G';
1817 sprintf(str, "%4d %cB%s", kb, unit,
1818 toosmall ? ", too small for MINIX 3" : "");
1819 return str;
1822 void
1823 printregions(region_t *theregions, int indent, int p_nr_partitions, int p_free_regions, int p_nr_regions, int numbers)
1825 int r, nofree = 0;
1826 region_t *reg;
1827 reg = theregions;
1829 if((p_nr_partitions >= NR_PARTITIONS || !p_free_regions) && p_free_regions)
1830 nofree = 1;
1831 for(r = 0; r < p_nr_regions; r++, reg++) {
1832 unsigned long units;
1833 if(reg->is_used_part) {
1834 char *name;
1835 name = typ2txt(reg->used_part.sysind);
1836 printf("%*s", indent, ""); type2col(reg->used_part.sysind);
1837 if(numbers) printf("[%d] ", r);
1838 printf("In use by %-10s ", name);
1839 units = reg->used_part.size / 2;
1840 col(0);
1841 printf(" (%s)\n", prettysizeprint(units));
1842 } else {
1843 printf("%*s", indent, "");
1844 if(numbers) {
1845 if(!nofree) printf("[%d] ", r);
1846 else printf("[-] ");
1848 printf("Free space ");
1849 units = ((reg->free_sec_last - reg->free_sec_start+1))/2;
1850 printf(" (%s)\n", prettysizeprint(units));
1854 if(numbers && p_nr_partitions >= NR_PARTITIONS && p_free_regions) {
1855 printf(
1856 "\nNote: there is free space on this disk, but you can't select it,\n"
1857 "because there isn't a free slot in the partition table to use it.\n"
1858 "You can reclaim the free space by deleting an adjacent region.\n");
1861 return;
1864 #define IS_YES 3
1865 #define IS_NO 4
1866 #define IS_OTHER 5
1868 is_sure(char *fmt, ...)
1870 char yesno[10];
1871 va_list ap;
1872 va_start (ap, fmt);
1873 vprintf(fmt, ap);
1874 va_end(ap);
1875 printf(" Please enter 'yes' or 'no': ");
1876 fflush(stdout);
1877 if(!fgets(yesno, sizeof(yesno)-1, stdin)) exit(1);
1879 if (strcmp(yesno, "yes\n") == 0) return(IS_YES);
1880 if (strcmp(yesno, "no\n") == 0) return(IS_NO);
1881 return IS_OTHER;
1884 void warn(char *message)
1886 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);
1890 may_kill_region(void)
1892 int confirmation;
1893 char line[100];
1894 int r, i;
1896 if(used_regions < 1) return 1;
1898 printf("\n -- Delete in-use region? --\n\n");
1900 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1901 printf("\nEnter the region number to delete or ENTER to continue: ");
1902 fflush(NULL);
1903 fgets(line, sizeof(line)-2, stdin);
1904 if(!isdigit(line[0]))
1905 return 1;
1907 r=atoi(line);
1908 if(r < 0 || r >= nr_regions) {
1909 printf("This choice is out of range.\n");
1910 return 0;
1913 if(!regions[r].is_used_part) {
1914 printf("This region is not in use.\n");
1915 return 0;
1918 i = regions[r].tableno;
1920 printf("\nPlease confirm that you want to delete region %d, losing all data it", r);
1921 printf("\ncontains. You're disk is not actually updated right away, but still.");
1922 printf("\n\n");
1924 do {
1925 confirmation = is_sure("Are you sure you want to continue?");
1926 if (confirmation == IS_NO) return 0;
1927 } while (confirmation != IS_YES);
1929 table[i].sysind = NO_PART;
1930 dirty = 1;
1931 regionize();
1933 /* User may go again. */
1934 return 0;
1938 region_t *
1939 select_region(void)
1941 int rn, done = 0;
1942 static char line[100];
1943 int nofree = 0;
1945 printstep(2, "Select a disk region");
1947 if(nr_regions < 1) {
1948 printf("\nNo regions found - maybe the drive is too small.\n"
1949 "Please try expert mode.\n");
1950 exit(1);
1953 if(nr_partitions >= NR_PARTITIONS || !free_regions) {
1954 if(free_regions) {
1955 nofree = 1;
1960 printf("\nPlease select the region that you want to use for the MINIX 3 setup.");
1961 printf("\nIf you select an in-use region it will be overwritten by MINIX. The");
1962 printf("\nfollowing region%s were found on the selected disk:\n\n",
1963 SORNOT(nr_regions));
1964 printregions(regions, 3, nr_partitions, free_regions, nr_regions, 1);
1967 printf("\n");
1968 do {
1969 printf("Enter the region number to use or type 'delete': ");
1970 if(nr_regions == 1) printf(" [0] ");
1971 fflush(NULL);
1973 if(!fgets(line, sizeof(line)-2, stdin))
1974 exit(1);
1976 if (nr_regions == 1 && line[0] == '\n') {
1977 rn = 0;
1978 done = 1;
1980 else {
1981 if(strcmp(line,"delete\n") == 0) {
1982 may_kill_region();
1983 return NULL;
1986 if(sscanf(line, "%d", &rn) != 1) {
1987 warn("invalid choice");
1988 continue;
1991 if(rn < 0 || rn >= nr_regions) {
1992 warn("out of range");
1993 continue;
1996 if(nofree && !regions[rn].is_used_part) {
1997 warn("not available");
1998 continue;
2001 done = 1;
2003 } while(! done);
2005 return(&regions[rn]);
2008 void printstep(int step, char *str)
2010 int n;
2011 n = printf("\n --- Substep 3.%d: %s ---", step, str);
2012 while(n++ < 73) printf("-");
2013 printf("\n");
2016 device_t *
2017 select_disk(void)
2019 int done = 0;
2020 int i, choice, drives;
2021 static char line[500];
2022 int biosdrive = 0;
2024 printstep(1, "Select a disk to install MINIX 3");
2025 printf("\nProbing for disks. This may take a short while.");
2027 i = 0;
2028 curdev=firstdev;
2030 for(; i < MAX_DEVICES;) {
2031 printf(".");
2032 fflush(stdout);
2033 m_read('r', &biosdrive);
2034 if(device >= 0) {
2035 devices[i].dev = curdev;
2036 devices[i].free_regions = free_regions;
2037 devices[i].nr_regions = nr_regions;
2038 devices[i].nr_partitions = nr_partitions;
2039 devices[i].used_regions = used_regions;
2040 devices[i].sectors = table[0].size;
2041 curdev->biosdrive = biosdrive-1;
2042 memcpy(devices[i].regions, regions, sizeof(regions));
2043 i++;
2046 nextdevice(NULL, 1);
2047 if(curdev == firstdev)
2048 break;
2051 drives = i;
2053 if(drives < 1) {
2054 printf("\nFound no drives - can't partition.\n");
2055 exit(1);
2058 printf(" Probing done.\n");
2059 printf("The following disk%s %s found on your system:\n\n", SORNOT(drives),
2060 drives == 1 ? "was" : "were");
2062 for(i = 0; i < drives; i++) {
2063 printf(" ");
2064 printf("Disk [%d]: ", i);
2065 printf("%s, ", devices[i].dev->name);
2066 printf("%s\n", prettysizeprint(devices[i].sectors/2));
2067 printregions(devices[i].regions, 8,
2068 devices[i].nr_partitions,
2069 devices[i].free_regions,
2070 devices[i].nr_regions, 0);
2073 printf("\n");
2074 do {
2075 printf("Enter the disk number to use: ");
2076 if (drives == 1) printf("[0] ");
2077 fflush(NULL);
2078 if(!fgets(line, sizeof(line)-2, stdin))
2079 exit(1);
2080 if (line[0] == '\n' && drives == 1) {
2081 choice = 0;
2082 done = 1;
2083 } else {
2084 if(sscanf(line, "%d", &choice) != 1) {
2085 warn("choose a disk");
2086 continue;
2088 if(choice < 0 || choice >= i) {
2089 warn("out of range");
2090 continue;
2092 done = 1;
2094 } while(! done);
2095 return devices[choice].dev;
2099 scribble_region(region_t *reg, struct part_entry **pe, int *made_new)
2101 int ex, changed = 0, i;
2102 struct part_entry *newpart;
2103 if(!reg->is_used_part) {
2104 ex = reg->free_sec_last - reg->free_sec_start + 1;
2105 if(made_new) *made_new = 1;
2106 } else if(made_new) *made_new = 0;
2107 if(!reg->is_used_part) {
2108 for(i = 1; i <= NR_PARTITIONS; i++)
2109 if(table[i].sysind == NO_PART)
2110 break;
2111 if(i > NR_PARTITIONS) {
2112 /* Bug, should've been caught earlier. */
2113 printf("Couldn't find a free slot. Please try expert mode.\n");
2114 exit(1);
2116 newpart = &table[i];
2117 newpart->lowsec = reg->free_sec_start;
2118 newpart->size = reg->free_sec_last - reg->free_sec_start + 1;
2119 changed = 1;
2120 newpart->sysind = MINIX_PART;
2121 } else {
2122 newpart = &reg->used_part;
2124 *pe = newpart;
2125 changed = 1;
2126 dirty = 1;
2127 return changed;
2131 sanitycheck_failed(char *dev, struct part_entry *pe)
2133 struct partition part;
2134 int fd;
2135 unsigned long it_lowsec, it_secsize;
2137 if((fd = open(dev, O_RDONLY)) < 0) {
2138 perror(dev);
2139 return 1;
2142 if (ioctl(fd, DIOCGETP, &part) < 0) {
2143 fprintf(stderr, "DIOCGETP failed\n");
2144 perror(dev);
2145 return 1;
2148 if(!open_ct_ok(fd)) {
2149 printf("\nAutopart error: the disk is in use. This means that although a\n"
2150 "new table has been written, it won't be in use by the system\n"
2151 "until it's no longer in use (or a reboot is done). Just in case,\n"
2152 "I'm not going to continue. Please un-use the disk (or reboot) and try\n"
2153 "again.\n\n");
2154 return 1;
2157 close(fd);
2159 it_lowsec = div64u(part.base, SECTOR_SIZE);
2160 it_secsize = div64u(part.size, SECTOR_SIZE);
2162 if(it_lowsec != pe->lowsec || it_secsize != pe->size) {
2163 fprintf(stderr, "\nReturned and set numbers don't match up!\n");
2164 fprintf(stderr, "This can happen if the disk is still opened.\n");
2165 return 1;
2168 return 0;
2172 do_autopart(int resultfd)
2174 int confirmation;
2175 region_t *r;
2176 struct part_entry *pe;
2177 struct part_entry orig_table[1 + NR_PARTITIONS];
2178 int region, newp;
2180 nordonly = 1;
2182 do {
2183 curdev = select_disk();
2184 } while(!curdev);
2186 if(device >= 0) {
2187 close(device);
2188 device = -1;
2190 recompute0();
2192 m_read('r', NULL);
2194 memcpy(orig_table, table, sizeof(table));
2196 do {
2197 /* Show regions. */
2198 r = select_region();
2199 } while(!r); /* Back to step 2. */
2201 /* Write things. */
2202 if(scribble_region(r, &pe, &newp)) {
2203 char *name;
2204 int i, found = -1;
2205 char partbuf[100], devname[100];
2206 struct part_entry *tpe = NULL;
2208 printstep(3, "Confirm your choices");
2210 region = (int)(r-regions);
2211 /* disk = (int) (curdev-devices); */
2213 printf("\nThis is the point of no return. You have selected to install MINIX 3\n");
2214 printf("into region %d of disk %s. Please confirm that you want\n",
2215 region, curdev->name);
2216 printf("to use this selection to install MINIX 3.\n\n");
2218 do {
2219 confirmation = is_sure("Are you sure you want to continue?");
2220 if (confirmation == IS_NO) return 1;
2221 } while (confirmation != IS_YES);
2223 /* Retrieve partition number in sorted order that we
2224 * have scribbled in.
2226 sort();
2227 for(i = 1; i <= NR_PARTITIONS; i++) {
2228 int si;
2229 si = sort_order[i];
2230 if(si < 1 || si > NR_PARTITIONS) {
2231 fprintf(stderr, "Autopart internal error (out of range) (nothing written).\n");
2232 exit(1);
2234 if(table[si].lowsec == pe->lowsec) {
2235 if(found > 0) {
2236 fprintf(stderr, "Autopart internal error (part found twice) (nothing written).\n");
2237 exit(1);
2239 check_ind(&table[si]);
2240 table[si].sysind = MINIX_PART;
2241 found = i;
2242 tpe = &table[si];
2245 if(found < 1) {
2246 fprintf(stderr, "Autopart internal error (part not found) (nothing written).\n");
2247 exit(1);
2249 m_write('w', NULL);
2250 if(dirty) {
2251 fprintf(stderr, "Autopart internal error (couldn't update disk).\n");
2252 exit(1);
2254 name=strrchr(curdev->name, '/');
2255 if(!name) name = curdev->name;
2256 else name++;
2258 sprintf(partbuf, "%sp%d d%dp%d\n", name, found-1,
2259 curdev->biosdrive, found-1);
2260 sprintf(devname, "/dev/%sp%d", name, found-1);
2261 if(resultfd >= 0 && write(resultfd, partbuf, strlen(partbuf)) < strlen(partbuf)) {
2262 fprintf(stderr, "Autopart internal error (couldn't write result).\n");
2263 exit(1);
2265 if(device >= 0) {
2266 close(device);
2267 device = -1;
2270 #if 0
2271 m_dump(orig_table);
2272 printf("\n");
2273 m_dump(table);
2274 #endif
2276 if(sanitycheck_failed(devname, tpe)) {
2277 fprintf(stderr, "Autopart internal error (disk sanity check failed).\n");
2278 exit(1);
2281 if(newp) {
2282 int fd;
2283 if((fd=open(devname, O_WRONLY)) < 0) {
2284 perror(devname);
2285 } else {
2286 /* Clear any subpartitioning. */
2287 static unsigned char sub[2048];
2288 sub[510] = 0x55;
2289 sub[511] = 0xAA;
2290 write(fd, sub, sizeof(sub));
2291 close(fd);
2294 return 0;
2297 return 1;
2300 int main(int argc, char **argv)
2302 int c;
2303 int i, key;
2304 int resultfd = -1;
2306 /* autopart uses getopt() */
2307 while((c = getopt(argc, argv, "m:f:")) != EOF) {
2308 switch(c) {
2309 case 'm':
2310 min_region_mb = atoi(optarg);
2311 break;
2312 case 'f':
2313 /* Make sure old data file is gone. */
2314 unlink(optarg);
2315 if((resultfd=open(optarg, O_CREAT | O_WRONLY | O_TRUNC)) < 0) {
2316 perror(optarg);
2317 return 1;
2319 sync(); /* Make sure no old data file lingers. */
2320 break;
2321 default:
2322 fprintf(stderr, "Unknown option\n");
2323 return 1;
2327 argc -= optind;
2328 argv += optind;
2330 for (i= 0; i < argc; i++) {
2331 newdevice(argv[i], 0, 0);
2334 if (firstdev == nil) {
2335 getdevices();
2336 key= ctrl('L');
2337 } else {
2338 key= 'r';
2342 int r;
2343 if (firstdev == nil) {
2344 fprintf(stderr, "autopart couldn't find any devices.\n");
2345 return 1;
2347 r = do_autopart(resultfd);
2348 if(resultfd >= 0) { close(resultfd); }
2349 return r;
2352 exit(0);