xref: /plan9/sys/src/cmd/disk/prep/fdisk.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
1 /*
2  * fdisk - edit dos disk partition table
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 #include <ctype.h>
8 #include <disk.h>
9 #include "edit.h"
10 
11 typedef struct Dospart	Dospart;
12 enum {
13 	NTentry = 4,
14 	Mpart = 64,
15 };
16 
17 static void rdpart(Edit*, uvlong, uvlong);
18 static void findmbr(Edit*);
19 static void autopart(Edit*);
20 static void wrpart(Edit*);
21 static void blankpart(Edit*);
22 static void cmdnamectl(Edit*);
23 static void recover(Edit*);
24 static int Dfmt(Fmt*);
25 static int blank;
26 static int dowrite;
27 static int file;
28 static int rdonly;
29 static int doauto;
30 static vlong mbroffset;
31 static int printflag;
32 static int printchs;
33 static int sec2cyl;
34 static int written;
35 
36 static void 	cmdsum(Edit*, Part*, vlong, vlong);
37 static char 	*cmdadd(Edit*, char*, vlong, vlong);
38 static char 	*cmddel(Edit*, Part*);
39 static char 	*cmdext(Edit*, int, char**);
40 static char 	*cmdhelp(Edit*);
41 static char 	*cmdokname(Edit*, char*);
42 static char 	*cmdwrite(Edit*);
43 static void	cmdprintctl(Edit*, int);
44 
45 #pragma varargck type "D" uchar*
46 
47 Edit edit = {
48 	.add=	cmdadd,
49 	.del=		cmddel,
50 	.ext=		cmdext,
51 	.help=	cmdhelp,
52 	.okname=	cmdokname,
53 	.sum=	cmdsum,
54 	.write=	cmdwrite,
55 	.printctl=	cmdprintctl,
56 
57 	.unit=	"cylinder",
58 };
59 
60 /*
61  * Catch the obvious error routines to fix up the disk.
62  */
63 void
64 sysfatal(char *fmt, ...)
65 {
66 	char buf[1024];
67 	va_list arg;
68 
69 	va_start(arg, fmt);
70 	vseprint(buf, buf+sizeof(buf), fmt, arg);
71 	va_end(arg);
72 	if(argv0)
73 		fprint(2, "%s: %s\n", argv0, buf);
74 	else
75 		fprint(2, "%s\n", buf);
76 
77 	if(written)
78 		recover(&edit);
79 
80 	exits(buf);
81 }
82 
83 void
84 abort(void)
85 {
86 	fprint(2, "abort\n");
87 	recover(&edit);
88 }
89 
90 void
91 usage(void)
92 {
93 	fprint(2, "usage: disk/fdisk [-abfprvw] [-s sectorsize] /dev/sdC0/data\n");
94 	exits("usage");
95 }
96 
97 void
98 main(int argc, char **argv)
99 {
100 	vlong secsize;
101 
102 	secsize = 0;
103 	ARGBEGIN{
104 	case 'a':
105 		doauto++;
106 		break;
107 	case 'b':
108 		blank++;
109 		break;
110 	case 'f':
111 		file++;
112 		break;
113 	case 'p':
114 		printflag++;
115 		break;
116 	case 'r':
117 		rdonly++;
118 		break;
119 	case 's':
120 		secsize = atoi(ARGF());
121 		break;
122 	case 'v':
123 		printchs++;
124 		break;
125 	case 'w':
126 		dowrite++;
127 		break;
128 	}ARGEND;
129 
130 	fmtinstall('D', Dfmt);
131 
132 	if(argc != 1)
133 		usage();
134 
135 	edit.disk = opendisk(argv[0], rdonly, file);
136 	if(edit.disk == nil) {
137 		fprint(2, "cannot open disk: %r\n");
138 		exits("opendisk");
139 	}
140 
141 	if(secsize != 0) {
142 		edit.disk->secsize = secsize;
143 		edit.disk->secs = edit.disk->size / secsize;
144 	}
145 
146 	sec2cyl = edit.disk->h * edit.disk->s;
147 	edit.end = edit.disk->secs / sec2cyl;
148 
149 	findmbr(&edit);
150 
151 	if(blank)
152 		blankpart(&edit);
153 	else
154 		rdpart(&edit, 0, 0);
155 
156 	if(doauto)
157 		autopart(&edit);
158 
159 	if(dowrite)
160 		runcmd(&edit, "w");
161 
162 	if(printflag)
163 		runcmd(&edit, "P");
164 
165 	if(dowrite || printflag)
166 		exits(0);
167 
168 	fprint(2, "cylinder = %lld bytes\n", sec2cyl*edit.disk->secsize);
169 	runcmd(&edit, "p");
170 	for(;;) {
171 		fprint(2, ">>> ");
172 		runcmd(&edit, getline(&edit));
173 	}
174 }
175 
176 typedef struct Tentry	Tentry;
177 typedef struct Table	Table;
178 typedef struct Type	Type;
179 typedef struct Tab	Tab;
180 typedef struct Recover Recover;
181 
182 struct Tentry {
183 	uchar	active;			/* active flag */
184 	uchar	starth;			/* starting head */
185 	uchar	starts;			/* starting sector */
186 	uchar	startc;			/* starting cylinder */
187 	uchar	type;			/* partition type */
188 	uchar	endh;			/* ending head */
189 	uchar	ends;			/* ending sector */
190 	uchar	endc;			/* ending cylinder */
191 	uchar	xlba[4];		/* starting LBA from beginning of disc or ext. partition */
192 	uchar	xsize[4];		/* size in sectors */
193 };
194 
195 enum {
196 	Active		= 0x80,		/* partition is active */
197 	Primary		= 0x01,		/* internal flag */
198 
199 	TypeBB		= 0xFF,
200 
201 	TypeEMPTY	= 0x00,
202 	TypeFAT12	= 0x01,
203 	TypeXENIX	= 0x02,		/* root */
204 	TypeXENIXUSR	= 0x03,		/* usr */
205 	TypeFAT16	= 0x04,
206 	TypeEXTENDED	= 0x05,
207 	TypeFATHUGE	= 0x06,
208 	TypeHPFS	= 0x07,
209 	TypeAIXBOOT	= 0x08,
210 	TypeAIXDATA	= 0x09,
211 	TypeOS2BOOT	= 0x0A,		/* OS/2 Boot Manager */
212 	TypeFAT32	= 0x0B,		/* FAT 32 */
213 	TypeFAT32LBA	= 0x0C,		/* FAT 32 needing LBA support */
214 	TypeFAT16X	= 0x0E,		/* FAT 16 needing LBA support */
215 	TypeEXTHUGE	= 0x0F,		/* FAT 32 extended partition */
216 	TypeUNFORMATTED	= 0x16,		/* unformatted primary partition (OS/2 FDISK)? */
217 	TypeHPFS2	= 0x17,
218 	TypeIBMRecovery = 0x1C,		/* really hidden fat */
219 	TypeCPM0	= 0x52,
220 	TypeDMDDO	= 0x54,		/* Disk Manager Dynamic Disk Overlay */
221 	TypeGB		= 0x56,		/* ???? */
222 	TypeSPEEDSTOR	= 0x61,
223 	TypeSYSV386	= 0x63,		/* also HURD? */
224 	TypeNETWARE	= 0x64,
225 	TypePCIX	= 0x75,
226 	TypeMINIX13	= 0x80,		/* Minix v1.3 and below */
227 	TypeMINIX	= 0x81,		/* Minix v1.5+ */
228 	TypeLINUXSWAP	= 0x82,
229 	TypeLINUX	= 0x83,
230 	TypeLINUXEXT	= 0x85,
231 	TypeLINUXLVM	= 0x8E,		/* logical volume manager */
232 	TypeAMOEBA	= 0x93,
233 	TypeAMOEBABB	= 0x94,
234 	TypeBSD386	= 0xA5,
235 	TypeNETBSD	= 0XA9,
236 	TypeBSDI	= 0xB7,
237 	TypeBSDISWAP	= 0xB8,
238 	TypeOTHER	= 0xDA,
239 	TypeCPM		= 0xDB,
240 	TypeDellRecovery= 0xDE,
241 	TypeSPEEDSTOR12	= 0xE1,
242 	TypeSPEEDSTOR16	= 0xE4,
243 	TypeLANSTEP	= 0xFE,
244 
245 	Type9		= 0x39,
246 
247 	Toffset		= 446,		/* offset of partition table in sector */
248 	Magic0		= 0x55,
249 	Magic1		= 0xAA,
250 };
251 
252 struct Table {
253 	Tentry	entry[NTentry];
254 	uchar	magic[2];
255 };
256 
257 struct Type {
258 	char *desc;
259 	char *name;
260 };
261 
262 struct Dospart {
263 	Part;
264 	Tentry;
265 
266 	u32int	lba;
267 	u32int	size;
268 	int		primary;
269 };
270 
271 struct Recover {
272 	Table	table;
273 	ulong	lba;
274 };
275 
276 static Type types[256] = {
277 	[TypeEMPTY]		{ "EMPTY", "" },
278 	[TypeFAT12]		{ "FAT12", "dos" },
279 	[TypeFAT16]		{ "FAT16", "dos" },
280 	[TypeFAT32]		{ "FAT32", "dos" },
281 	[TypeFAT32LBA]		{ "FAT32LBA", "dos" },
282 	[TypeFAT16X]		{ "FAT16X", "dos" },
283 	[TypeEXTHUGE]		{ "EXTHUGE", "" },
284 	[TypeIBMRecovery]	{ "IBMRECOVERY", "ibm" },
285 	[TypeEXTENDED]		{ "EXTENDED", "" },
286 	[TypeFATHUGE]		{ "FATHUGE", "dos" },
287 	[TypeBB]		{ "BB", "bb" },
288 
289 	[TypeXENIX]		{ "XENIX", "xenix" },
290 	[TypeXENIXUSR]		{ "XENIX USR", "xenixusr" },
291 	[TypeHPFS]		{ "HPFS", "ntfs" },
292 	[TypeAIXBOOT]		{ "AIXBOOT", "aixboot" },
293 	[TypeAIXDATA]		{ "AIXDATA", "aixdata" },
294 	[TypeOS2BOOT]		{ "OS/2BOOT", "os2boot" },
295 	[TypeUNFORMATTED]	{ "UNFORMATTED", "" },
296 	[TypeHPFS2]		{ "HPFS2", "hpfs2" },
297 	[TypeCPM0]		{ "CPM0", "cpm0" },
298 	[TypeDMDDO]		{ "DMDDO", "dmdd0" },
299 	[TypeGB]		{ "GB", "gb" },
300 	[TypeSPEEDSTOR]		{ "SPEEDSTOR", "speedstor" },
301 	[TypeSYSV386]		{ "SYSV386", "sysv386" },
302 	[TypeNETWARE]		{ "NETWARE", "netware" },
303 	[TypePCIX]		{ "PCIX", "pcix" },
304 	[TypeMINIX13]		{ "MINIXV1.3", "minix13" },
305 	[TypeMINIX]		{ "MINIXV1.5", "minix15" },
306 	[TypeLINUXSWAP]		{ "LINUXSWAP", "linuxswap" },
307 	[TypeLINUX]		{ "LINUX", "linux" },
308 	[TypeLINUXEXT]		{ "LINUXEXTENDED", "" },
309 	[TypeLINUXLVM]		{ "LINUXLVM", "linuxlvm" },
310 	[TypeAMOEBA]		{ "AMOEBA", "amoeba" },
311 	[TypeAMOEBABB]		{ "AMOEBABB", "amoebaboot" },
312 	[TypeBSD386]		{ "BSD386", "bsd386" },
313 	[TypeNETBSD]		{ "NETBSD", "netbsd" },
314 	[TypeBSDI]		{ "BSDI", "bsdi" },
315 	[TypeBSDISWAP]		{ "BSDISWAP", "bsdiswap" },
316 	[TypeOTHER]		{ "OTHER", "other" },
317 	[TypeCPM]		{ "CPM", "cpm" },
318 	[TypeDellRecovery]	{ "DELLRECOVERY", "dell" },
319 	[TypeSPEEDSTOR12]	{ "SPEEDSTOR12", "speedstor" },
320 	[TypeSPEEDSTOR16]	{ "SPEEDSTOR16", "speedstor" },
321 	[TypeLANSTEP]		{ "LANSTEP", "lanstep" },
322 
323 	[Type9]			{ "PLAN9", "plan9" },
324 };
325 
326 static Dospart	part[Mpart];
327 static int		npart;
328 
329 static char*
330 typestr0(int type)
331 {
332 	static char buf[100];
333 
334 	sprint(buf, "type %d", type);
335 	if(type < 0 || type >= 256)
336 		return buf;
337 	if(types[type].desc == nil)
338 		return buf;
339 	return types[type].desc;
340 }
341 
342 static u32int
343 getle32(void* v)
344 {
345 	uchar *p;
346 
347 	p = v;
348 	return (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0];
349 }
350 
351 static void
352 putle32(void* v, u32int i)
353 {
354 	uchar *p;
355 
356 	p = v;
357 	p[0] = i;
358 	p[1] = i>>8;
359 	p[2] = i>>16;
360 	p[3] = i>>24;
361 }
362 
363 static void
364 diskread(Disk *disk, void *data, int ndata, u32int sec, u32int off)
365 {
366 	if(seek(disk->fd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off)
367 		sysfatal("diskread seek %lud.%lud: %r", (ulong)sec, (ulong)off);
368 	if(readn(disk->fd, data, ndata) != ndata)
369 		sysfatal("diskread %lud at %lud.%lud: %r", (ulong)ndata, (ulong)sec, (ulong)off);
370 }
371 
372 static int
373 diskwrite(Disk *disk, void *data, int ndata, u32int sec, u32int off)
374 {
375 	written = 1;
376 	if(seek(disk->wfd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off)
377 		goto Error;
378 	if(write(disk->wfd, data, ndata) != ndata)
379 		goto Error;
380 	return 0;
381 
382 Error:
383 	fprint(2, "write %d bytes at %lud.%lud failed: %r\n", ndata, (ulong)sec, (ulong)off);
384 	return -1;
385 }
386 
387 static Dospart*
388 mkpart(char *name, int primary, vlong lba, vlong size, Tentry *t)
389 {
390 	static int n;
391 	Dospart *p;
392 
393 	p = emalloc(sizeof(*p));
394 	if(name)
395 		p->name = estrdup(name);
396 	else{
397 		p->name = emalloc(20);
398 		sprint(p->name, "%c%d", primary ? 'p' : 's', ++n);
399 	}
400 
401 	if(t)
402 		p->Tentry = *t;
403 	else
404 		memset(&p->Tentry, 0, sizeof(Tentry));
405 
406 	p->changed = 0;
407 	p->start = lba/sec2cyl;
408 	p->end = (lba+size)/sec2cyl;
409 	p->ctlstart = lba;
410 	p->ctlend = lba+size;
411 	p->lba = lba;
412 	if (p->lba != lba)
413 		fprint(2, "%s: start of partition (%lld) won't fit in MBR table\n", argv0, lba);
414 	p->size = size;
415 	if (p->size != size)
416 		fprint(2, "%s: size of partition (%lld) won't fit in MBR table\n", argv0, size);
417 	p->primary = primary;
418 	return p;
419 }
420 
421 /*
422  * Recovery takes care of remembering what the various tables
423  * looked like when we started, attempting to restore them when
424  * we are finished.
425  */
426 static Recover	*rtab;
427 static int		nrtab;
428 
429 static void
430 addrecover(Table t, ulong lba)
431 {
432 	if((nrtab%8) == 0) {
433 		rtab = realloc(rtab, (nrtab+8)*sizeof(rtab[0]));
434 		if(rtab == nil)
435 			sysfatal("out of memory");
436 	}
437 	rtab[nrtab] = (Recover){t, lba};
438 	nrtab++;
439 }
440 
441 static void
442 recover(Edit *edit)
443 {
444 	int err, i, ctlfd;
445 	vlong offset;
446 
447 	err = 0;
448 	for(i=0; i<nrtab; i++)
449 		if(diskwrite(edit->disk, &rtab[i].table, sizeof(Table), rtab[i].lba, Toffset) < 0)
450 			err = 1;
451 	if(err) {
452 		fprint(2, "warning: some writes failed during restoration of old partition tables\n");
453 		exits("inconsistent");
454 	} else {
455 		fprint(2, "restored old partition tables\n");
456 	}
457 
458 	ctlfd = edit->disk->ctlfd;
459 	offset = edit->disk->offset;
460 	if(ctlfd >= 0){
461 		for(i=0; i<edit->npart; i++)
462 			if(edit->part[i]->ctlname && fprint(ctlfd, "delpart %s", edit->part[i]->ctlname)<0)
463 				fprint(2, "delpart failed: %s: %r", edit->part[i]->ctlname);
464 		for(i=0; i<edit->nctlpart; i++)
465 			if(edit->part[i]->name && fprint(ctlfd, "delpart %s", edit->ctlpart[i]->name)<0)
466 				fprint(2, "delpart failed: %s: %r", edit->ctlpart[i]->name);
467 		for(i=0; i<edit->nctlpart; i++){
468 			if(fprint(ctlfd, "part %s %lld %lld", edit->ctlpart[i]->name,
469 				edit->ctlpart[i]->start+offset, edit->ctlpart[i]->end+offset) < 0){
470 				fprint(2, "restored disk partition table but not kernel; reboot\n");
471 				exits("inconsistent");
472 			}
473 		}
474 	}
475 	exits("restored");
476 
477 }
478 
479 /*
480  * Read the partition table (including extended partition tables)
481  * from the disk into the part array.
482  */
483 static void
484 rdpart(Edit *edit, uvlong lba, uvlong xbase)
485 {
486 	char *err;
487 	Table table;
488 	Tentry *tp, *ep;
489 	Dospart *p;
490 
491 	if(xbase == 0)
492 		xbase = lba;
493 
494 	diskread(edit->disk, &table, sizeof table, mbroffset+lba, Toffset);
495 	addrecover(table, mbroffset+lba);
496 
497 	if(table.magic[0] != Magic0 || table.magic[1] != Magic1) {
498 		assert(lba != 0);
499 		return;
500 	}
501 
502 	for(tp=table.entry, ep=tp+NTentry; tp<ep && npart < Mpart; tp++) {
503 		switch(tp->type) {
504 		case TypeEMPTY:
505 			break;
506 		case TypeEXTENDED:
507 		case TypeEXTHUGE:
508 		case TypeLINUXEXT:
509 			rdpart(edit, xbase+getle32(tp->xlba), xbase);
510 			break;
511 		default:
512 			p = mkpart(nil, lba==0, lba+getle32(tp->xlba), getle32(tp->xsize), tp);
513 			if(err = addpart(edit, p))
514 				fprint(2, "adding partition: %s\n", err);
515 			break;
516 		}
517 	}
518 }
519 
520 static void
521 blankpart(Edit *edit)
522 {
523 	edit->changed = 1;
524 }
525 
526 static void
527 findmbr(Edit *edit)
528 {
529 	Table table;
530 	Tentry *tp;
531 
532 	diskread(edit->disk, &table, sizeof(Table), 0, Toffset);
533 	if(table.magic[0] != Magic0 || table.magic[1] != Magic1)
534 		sysfatal("did not find master boot record");
535 
536 	for(tp = table.entry; tp < &table.entry[NTentry]; tp++)
537 		if(tp->type == TypeDMDDO)
538 			mbroffset = edit->disk->s;
539 }
540 
541 static int
542 haveroom(Edit *edit, int primary, vlong start)
543 {
544 	int i, lastsec, n;
545 	Dospart *p, *q;
546 	ulong pend, qstart;
547 
548 	if(primary) {
549 		/*
550 		 * must be open primary slot.
551 		 * primary slots are taken by primary partitions
552 		 * and runs of secondary partitions.
553 		 */
554 		n = 0;
555 		lastsec = 0;
556 		for(i=0; i<edit->npart; i++) {
557 			p = (Dospart*)edit->part[i];
558 			if(p->primary)
559 				n++, lastsec=0;
560 			else if(!lastsec)
561 				n++, lastsec=1;
562 		}
563 		return n<4;
564 	}
565 
566 	/*
567 	 * secondary partitions can be inserted between two primary
568 	 * partitions only if there is an empty primary slot.
569 	 * otherwise, we can put a new secondary partition next
570 	 * to a secondary partition no problem.
571 	 */
572 	n = 0;
573 	for(i=0; i<edit->npart; i++){
574 		p = (Dospart*)edit->part[i];
575 		if(p->primary)
576 			n++;
577 		pend = p->end;
578 		if(i+1<edit->npart){
579 			q = (Dospart*)edit->part[i+1];
580 			qstart = q->start;
581 		}else{
582 			qstart = edit->end;
583 			q = nil;
584 		}
585 		if(start < pend || start >= qstart)
586 			continue;
587 		/* we go between these two */
588 		if(p->primary==0 || (q && q->primary==0))
589 			return 1;
590 	}
591 	/* not next to a secondary, need a new primary */
592 	return n<4;
593 }
594 
595 static void
596 autopart(Edit *edit)
597 {
598 	char *err;
599 	int active, i;
600 	vlong bigstart, bigsize, start;
601 	Dospart *p;
602 
603 	for(i=0; i<edit->npart; i++)
604 		if(((Dospart*)edit->part[i])->type == Type9)
605 			return;
606 
607 	/* look for the biggest gap in which we can put a primary partition */
608 	start = 0;
609 	bigsize = 0;
610 	SET(bigstart);
611 	for(i=0; i<edit->npart; i++) {
612 		p = (Dospart*)edit->part[i];
613 		if(p->start > start && p->start - start > bigsize && haveroom(edit, 1, start)) {
614 			bigsize = p->start - start;
615 			bigstart = start;
616 		}
617 		start = p->end;
618 	}
619 
620 	if(edit->end - start > bigsize && haveroom(edit, 1, start)) {
621 		bigsize = edit->end - start;
622 		bigstart = start;
623 	}
624 	if(bigsize < 1) {
625 		fprint(2, "couldn't find space or partition slot for plan 9 partition\n");
626 		return;
627 	}
628 
629 	/* set new partition active only if no others are */
630 	active = Active;
631 	for(i=0; i<edit->npart; i++)
632 		if(((Dospart*)edit->part[i])->primary && (((Dospart*)edit->part[i])->active & Active))
633 			active = 0;
634 
635 	/* add new plan 9 partition */
636 	bigsize *= sec2cyl;
637 	bigstart *= sec2cyl;
638 	if(bigstart == 0) {
639 		bigstart += edit->disk->s;
640 		bigsize -= edit->disk->s;
641 	}
642 	p = mkpart(nil, 1, bigstart, bigsize, nil);
643 	p->active = active;
644 	p->changed = 1;
645 	p->type = Type9;
646 	edit->changed = 1;
647 	if(err = addpart(edit, p)) {
648 		fprint(2, "error adding plan9 partition: %s\n", err);
649 		return;
650 	}
651 }
652 
653 typedef struct Name Name;
654 struct Name {
655 	char *name;
656 	Name *link;
657 };
658 Name *namelist;
659 static void
660 plan9print(Dospart *part, int fd)
661 {
662 	int i, ok;
663 	char *name, *vname;
664 	Name *n;
665 	vlong start, end;
666 	char *sep;
667 
668 	vname = types[part->type].name;
669 	if(vname==nil || strcmp(vname, "")==0) {
670 		part->ctlname = "";
671 		return;
672 	}
673 
674 	start = mbroffset+part->lba;
675 	end = start+part->size;
676 
677 	/* avoid names like plan90 */
678 	i = strlen(vname) - 1;
679 	if(vname[i] >= '0' && vname[i] <= '9')
680 		sep = ".";
681 	else
682 		sep = "";
683 
684 	i = 0;
685 	name = emalloc(strlen(vname)+10);
686 
687 	sprint(name, "%s", vname);
688 	do {
689 		ok = 1;
690 		for(n=namelist; n; n=n->link) {
691 			if(strcmp(name, n->name) == 0) {
692 				i++;
693 				sprint(name, "%s%s%d", vname, sep, i);
694 				ok = 0;
695 			}
696 		}
697 	} while(ok == 0);
698 
699 	n = emalloc(sizeof(*n));
700 	n->name = name;
701 	n->link = namelist;
702 	namelist = n;
703 	part->ctlname = name;
704 
705 	if(fd >= 0)
706 		print("part %s %lld %lld\n", name, start, end);
707 }
708 
709 static void
710 freenamelist(void)
711 {
712 	Name *n, *next;
713 
714 	for(n=namelist; n; n=next) {
715 		next = n->link;
716 		free(n);
717 	}
718 	namelist = nil;
719 }
720 
721 static void
722 cmdprintctl(Edit *edit, int ctlfd)
723 {
724 	int i;
725 
726 	freenamelist();
727 	for(i=0; i<edit->npart; i++)
728 		plan9print((Dospart*)edit->part[i], -1);
729 	ctldiff(edit, ctlfd);
730 }
731 
732 static char*
733 cmdokname(Edit*, char *name)
734 {
735 	char *q;
736 
737 	if(name[0] != 'p' && name[0] != 's')
738 		return "name must be pN or sN";
739 
740 	strtol(name+1, &q, 10);
741 	if(*q != '\0')
742 		return "name must be pN or sN";
743 
744 	return nil;
745 }
746 
747 #define TB (1024LL*GB)
748 #define GB (1024*1024*1024)
749 #define MB (1024*1024)
750 #define KB (1024)
751 
752 static void
753 cmdsum(Edit *edit, Part *vp, vlong a, vlong b)
754 {
755 	char *name, *ty;
756 	char buf[3];
757 	char *suf;
758 	Dospart *p;
759 	vlong sz, div;
760 
761 	p = (Dospart*)vp;
762 
763 	buf[0] = p && p->changed ? '\'' : ' ';
764 	buf[1] = p && (p->active & Active) ? '*' : ' ';
765 	buf[2] = '\0';
766 
767 	name = p ? p->name : "empty";
768 	ty = p ? typestr0(p->type) : "";
769 
770 	sz = (b-a)*edit->disk->secsize*sec2cyl;
771 	if(sz >= 1*TB){
772 		suf = "TB";
773 		div = TB;
774 	}else if(sz >= 1*GB){
775 		suf = "GB";
776 		div = GB;
777 	}else if(sz >= 1*MB){
778 		suf = "MB";
779 		div = MB;
780 	}else if(sz >= 1*KB){
781 		suf = "KB";
782 		div = KB;
783 	}else{
784 		suf = "B ";
785 		div = 1;
786 	}
787 
788 	if(div == 1)
789 		print("%s %-12s %*lld %-*lld (%lld cylinders, %lld %s) %s\n", buf, name,
790 			edit->disk->width, a, edit->disk->width, b, b-a, sz, suf, ty);
791 	else
792 		print("%s %-12s %*lld %-*lld (%lld cylinders, %lld.%.2d %s) %s\n", buf, name,
793 			edit->disk->width, a, edit->disk->width, b,  b-a,
794 			sz/div, (int)(((sz%div)*100)/div), suf, ty);
795 }
796 
797 static char*
798 cmdadd(Edit *edit, char *name, vlong start, vlong end)
799 {
800 	Dospart *p;
801 
802 	if(!haveroom(edit, name[0]=='p', start))
803 		return "no room for partition";
804 	start *= sec2cyl;
805 	end *= sec2cyl;
806 	if(start == 0 || name[0] != 'p')
807 		start += edit->disk->s;
808 	p = mkpart(name, name[0]=='p', start, end-start, nil);
809 	p->changed = 1;
810 	p->type = Type9;
811 	return addpart(edit, p);
812 }
813 
814 static char*
815 cmddel(Edit *edit, Part *p)
816 {
817 	return delpart(edit, p);
818 }
819 
820 static char*
821 cmdwrite(Edit *edit)
822 {
823 	wrpart(edit);
824 	return nil;
825 }
826 
827 static char *help =
828 	"A name - set partition active\n"
829 	"P - print table in ctl format\n"
830 	"R - restore disk back to initial configuration and exit\n"
831 	"e - show empty dos partitions\n"
832 	"t name [type] - set partition type\n";
833 
834 static char*
835 cmdhelp(Edit*)
836 {
837 	print("%s\n", help);
838 	return nil;
839 }
840 
841 static char*
842 cmdactive(Edit *edit, int nf, char **f)
843 {
844 	int i;
845 	Dospart *p, *ip;
846 
847 	if(nf != 2)
848 		return "args";
849 
850 	if(f[1][0] != 'p')
851 		return "cannot set secondary partition active";
852 
853 	if((p = (Dospart*)findpart(edit, f[1])) == nil)
854 		return "unknown partition";
855 
856 	for(i=0; i<edit->npart; i++) {
857 		ip = (Dospart*)edit->part[i];
858 		if(ip->active & Active) {
859 			ip->active &= ~Active;
860 			ip->changed = 1;
861 			edit->changed = 1;
862 		}
863 	}
864 
865 	if((p->active & Active) == 0) {
866 		p->active |= Active;
867 		p->changed = 1;
868 		edit->changed = 1;
869 	}
870 
871 	return nil;
872 }
873 
874 static char*
875 strupr(char *s)
876 {
877 	char *p;
878 
879 	for(p=s; *p; p++)
880 		*p = toupper(*p);
881 	return s;
882 }
883 
884 static void
885 dumplist(void)
886 {
887 	int i, n;
888 
889 	n = 0;
890 	for(i=0; i<256; i++) {
891 		if(types[i].desc) {
892 			print("%-16s", types[i].desc);
893 			if(n++%4 == 3)
894 				print("\n");
895 		}
896 	}
897 	if(n%4)
898 		print("\n");
899 }
900 
901 static char*
902 cmdtype(Edit *edit, int nf, char **f)
903 {
904 	char *q;
905 	Dospart *p;
906 	int i;
907 
908 	if(nf < 2)
909 		return "args";
910 
911 	if((p = (Dospart*)findpart(edit, f[1])) == nil)
912 		return "unknown partition";
913 
914 	if(nf == 2) {
915 		for(;;) {
916 			fprint(2, "new partition type [? for list]: ");
917 			q = getline(edit);
918 			if(q[0] == '?')
919 				dumplist();
920 			else
921 				break;
922 		}
923 	} else
924 		q = f[2];
925 
926 	strupr(q);
927 	for(i=0; i<256; i++)
928 		if(types[i].desc && strcmp(types[i].desc, q) == 0)
929 			break;
930 	if(i < 256 && p->type != i) {
931 		p->type = i;
932 		p->changed = 1;
933 		edit->changed = 1;
934 	}
935 	return nil;
936 }
937 
938 static char*
939 cmdext(Edit *edit, int nf, char **f)
940 {
941 	switch(f[0][0]) {
942 	case 'A':
943 		return cmdactive(edit, nf, f);
944 	case 't':
945 		return cmdtype(edit, nf, f);
946 	case 'R':
947 		recover(edit);
948 		return nil;
949 	default:
950 		return "unknown command";
951 	}
952 }
953 
954 static int
955 Dfmt(Fmt *f)
956 {
957 	char buf[60];
958 	uchar *p;
959 	int c, h, s;
960 
961 	p = va_arg(f->args, uchar*);
962 	h = p[0];
963 	c = p[2];
964 	c |= (p[1]&0xC0)<<2;
965 	s = (p[1] & 0x3F);
966 
967 	sprint(buf, "%d/%d/%d", c, h, s);
968 	return fmtstrcpy(f, buf);
969 }
970 
971 static void
972 writechs(Disk *disk, uchar *p, vlong lba)
973 {
974 	int c, h, s;
975 
976 	s = lba % disk->s;
977 	h = (lba / disk->s) % disk->h;
978 	c = lba / (disk->s * disk->h);
979 
980 	if(c >= 1024) {
981 		c = 1023;
982 		h = disk->h - 1;
983 		s = disk->s - 1;
984 	}
985 
986 	p[0] = h;
987 	p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0);
988 	p[2] = c;
989 }
990 
991 static void
992 wrtentry(Disk *disk, Tentry *tp, int type, u32int xbase, u32int lba, u32int end)
993 {
994 	tp->type = type;
995 	writechs(disk, &tp->starth, lba);
996 	writechs(disk, &tp->endh, end-1);
997 	putle32(tp->xlba, lba-xbase);
998 	putle32(tp->xsize, end-lba);
999 }
1000 
1001 static int
1002 wrextend(Edit *edit, int i, vlong xbase, vlong startlba, vlong *endlba)
1003 {
1004 	int ni;
1005 	Table table;
1006 	Tentry *tp, *ep;
1007 	Dospart *p;
1008 	Disk *disk;
1009 
1010 	if(i == edit->npart){
1011 		*endlba = edit->disk->secs;
1012 	Finish:
1013 		if(startlba < *endlba){
1014 			disk = edit->disk;
1015 			diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset);
1016 			tp = table.entry;
1017 			ep = tp+NTentry;
1018 			for(; tp<ep; tp++)
1019 				memset(tp, 0, sizeof *tp);
1020 			table.magic[0] = Magic0;
1021 			table.magic[1] = Magic1;
1022 
1023 			if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0)
1024 				recover(edit);
1025 		}
1026 		return i;
1027 	}
1028 
1029 	p = (Dospart*)edit->part[i];
1030 	if(p->primary){
1031 		*endlba = (vlong)p->start*sec2cyl;
1032 		goto Finish;
1033 	}
1034 
1035 	disk = edit->disk;
1036 	diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset);
1037 	tp = table.entry;
1038 	ep = tp+NTentry;
1039 
1040 	ni = wrextend(edit, i+1, xbase, p->end*sec2cyl, endlba);
1041 
1042 	*tp = p->Tentry;
1043 	wrtentry(disk, tp, p->type, startlba, startlba+disk->s, p->end*sec2cyl);
1044 	tp++;
1045 
1046 	if(p->end*sec2cyl != *endlba){
1047 		memset(tp, 0, sizeof *tp);
1048 		wrtentry(disk, tp, TypeEXTENDED, xbase, p->end*sec2cyl, *endlba);
1049 		tp++;
1050 	}
1051 
1052 	for(; tp<ep; tp++)
1053 		memset(tp, 0, sizeof *tp);
1054 
1055 	table.magic[0] = Magic0;
1056 	table.magic[1] = Magic1;
1057 
1058 	if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0)
1059 		recover(edit);
1060 	return ni;
1061 }
1062 
1063 static void
1064 wrpart(Edit *edit)
1065 {
1066 	int i, ni, t;
1067 	Table table;
1068 	Tentry *tp, *ep;
1069 	Disk *disk;
1070 	vlong s, endlba;
1071 	Dospart *p;
1072 
1073 	disk = edit->disk;
1074 
1075 	diskread(disk, &table, sizeof table, mbroffset, Toffset);
1076 
1077 	tp = table.entry;
1078 	ep = tp+NTentry;
1079 	for(i=0; i<edit->npart && tp<ep; ) {
1080 		p = (Dospart*)edit->part[i];
1081 		if(p->start == 0)
1082 			s = disk->s;
1083 		else
1084 			s = p->start*sec2cyl;
1085 		if(p->primary) {
1086 			*tp = p->Tentry;
1087 			wrtentry(disk, tp, p->type, 0, s, p->end*sec2cyl);
1088 			tp++;
1089 			i++;
1090 		} else {
1091 			ni = wrextend(edit, i, p->start*sec2cyl, p->start*sec2cyl, &endlba);
1092 			memset(tp, 0, sizeof *tp);
1093 			if(endlba >= 1024*sec2cyl)
1094 				t = TypeEXTHUGE;
1095 			else
1096 				t = TypeEXTENDED;
1097 			wrtentry(disk, tp, t, 0, s, endlba);
1098 			tp++;
1099 			i = ni;
1100 		}
1101 	}
1102 	for(; tp<ep; tp++)
1103 		memset(tp, 0, sizeof(*tp));
1104 
1105 	if(i != edit->npart)
1106 		sysfatal("cannot happen #1");
1107 
1108 	if(diskwrite(disk, &table, sizeof table, mbroffset, Toffset) < 0)
1109 		recover(edit);
1110 
1111 	/* bring parts up to date */
1112 	freenamelist();
1113 	for(i=0; i<edit->npart; i++)
1114 		plan9print((Dospart*)edit->part[i], -1);
1115 
1116 	if(ctldiff(edit, disk->ctlfd) < 0)
1117 		fprint(2, "?warning: partitions could not be updated in devsd\n");
1118 }
1119