xref: /plan9/sys/src/cmd/disk/prep/fdisk.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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*, ulong, ulong);
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 	TypeAMOEBA	= 0x93,
232 	TypeAMOEBABB	= 0x94,
233 	TypeBSD386	= 0xA5,
234 	TypeBSDI	= 0xB7,
235 	TypeBSDISWAP	= 0xB8,
236 	TypeOTHER	= 0xDA,
237 	TypeCPM		= 0xDB,
238 	TypeDellRecovery= 0xDE,
239 	TypeSPEEDSTOR12	= 0xE1,
240 	TypeSPEEDSTOR16	= 0xE4,
241 	TypeLANSTEP	= 0xFE,
242 
243 	Type9		= 0x39,
244 
245 	Toffset		= 446,		/* offset of partition table in sector */
246 	Magic0		= 0x55,
247 	Magic1		= 0xAA,
248 };
249 
250 struct Table {
251 	Tentry	entry[NTentry];
252 	uchar	magic[2];
253 };
254 
255 struct Type {
256 	char *desc;
257 	char *name;
258 };
259 
260 struct Dospart {
261 	Part;
262 	Tentry;
263 
264 	u32int	lba;
265 	u32int	size;
266 	int		primary;
267 };
268 
269 struct Recover {
270 	Table	table;
271 	ulong	lba;
272 };
273 
274 static Type types[256] = {
275 	[TypeEMPTY]		{ "EMPTY", "" },
276 	[TypeFAT12]		{ "FAT12", "dos" },
277 	[TypeFAT16]		{ "FAT16", "dos" },
278 	[TypeFAT32]		{ "FAT32", "dos" },
279 	[TypeFAT32LBA]		{ "FAT32LBA", "dos" },
280 	[TypeFAT16X]		{ "FAT16X", "dos" },
281 	[TypeEXTHUGE]		{ "EXTHUGE", "" },
282 	[TypeIBMRecovery]	{ "IBMRECOVERY", "ibm" },
283 	[TypeEXTENDED]		{ "EXTENDED", "" },
284 	[TypeFATHUGE]		{ "FATHUGE", "dos" },
285 	[TypeBB]		{ "BB", "bb" },
286 
287 	[TypeXENIX]		{ "XENIX", "xenix" },
288 	[TypeXENIXUSR]		{ "XENIX USR", "xenixusr" },
289 	[TypeHPFS]		{ "HPFS", "ntfs" },
290 	[TypeAIXBOOT]		{ "AIXBOOT", "aixboot" },
291 	[TypeAIXDATA]		{ "AIXDATA", "aixdata" },
292 	[TypeOS2BOOT]		{ "OS/2BOOT", "os2boot" },
293 	[TypeUNFORMATTED]	{ "UNFORMATTED", "" },
294 	[TypeHPFS2]		{ "HPFS2", "hpfs2" },
295 	[TypeCPM0]		{ "CPM0", "cpm0" },
296 	[TypeDMDDO]		{ "DMDDO", "dmdd0" },
297 	[TypeGB]		{ "GB", "gb" },
298 	[TypeSPEEDSTOR]		{ "SPEEDSTOR", "speedstor" },
299 	[TypeSYSV386]		{ "SYSV386", "sysv386" },
300 	[TypeNETWARE]		{ "NETWARE", "netware" },
301 	[TypePCIX]		{ "PCIX", "pcix" },
302 	[TypeMINIX13]		{ "MINIXV1.3", "minix13" },
303 	[TypeMINIX]		{ "MINIXV1.5", "minix15" },
304 	[TypeLINUXSWAP]		{ "LINUXSWAP", "linuxswap" },
305 	[TypeLINUX]		{ "LINUX", "linux" },
306 	[TypeLINUXEXT]		{ "LINUXEXTENDED", "" },
307 	[TypeAMOEBA]		{ "AMOEBA", "amoeba" },
308 	[TypeAMOEBABB]		{ "AMOEBABB", "amoebaboot" },
309 	[TypeBSD386]		{ "BSD386", "bsd386" },
310 	[TypeBSDI]		{ "BSDI", "bsdi" },
311 	[TypeBSDISWAP]		{ "BSDISWAP", "bsdiswap" },
312 	[TypeOTHER]		{ "OTHER", "other" },
313 	[TypeCPM]		{ "CPM", "cpm" },
314 	[TypeDellRecovery]	{ "DELLRECOVERY", "dell" },
315 	[TypeSPEEDSTOR12]	{ "SPEEDSTOR12", "speedstor" },
316 	[TypeSPEEDSTOR16]	{ "SPEEDSTOR16", "speedstor" },
317 	[TypeLANSTEP]		{ "LANSTEP", "lanstep" },
318 
319 	[Type9]			{ "PLAN9", "plan9" },
320 };
321 
322 static Dospart	part[Mpart];
323 static int		npart;
324 
325 static char*
326 typestr0(int type)
327 {
328 	static char buf[100];
329 
330 	sprint(buf, "type %d", type);
331 	if(type < 0 || type >= 256)
332 		return buf;
333 	if(types[type].desc == nil)
334 		return buf;
335 	return types[type].desc;
336 }
337 
338 static u32int
339 getle32(void* v)
340 {
341 	uchar *p;
342 
343 	p = v;
344 	return (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0];
345 }
346 
347 static void
348 putle32(void* v, u32int i)
349 {
350 	uchar *p;
351 
352 	p = v;
353 	p[0] = i;
354 	p[1] = i>>8;
355 	p[2] = i>>16;
356 	p[3] = i>>24;
357 }
358 
359 static void
360 diskread(Disk *disk, void *data, int ndata, u32int sec, u32int off)
361 {
362 	if(seek(disk->fd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off)
363 		sysfatal("diskread seek %lud.%lud: %r", (ulong)sec, (ulong)off);
364 	if(readn(disk->fd, data, ndata) != ndata)
365 		sysfatal("diskread %lud at %lud.%lud: %r", (ulong)ndata, (ulong)sec, (ulong)off);
366 }
367 
368 static int
369 diskwrite(Disk *disk, void *data, int ndata, u32int sec, u32int off)
370 {
371 	written = 1;
372 	if(seek(disk->wfd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off)
373 		goto Error;
374 	if(write(disk->wfd, data, ndata) != ndata)
375 		goto Error;
376 	return 0;
377 
378 Error:
379 	fprint(2, "write %d bytes at %lud.%lud failed: %r\n", ndata, (ulong)sec, (ulong)off);
380 	return -1;
381 }
382 
383 static Dospart*
384 mkpart(char *name, int primary, u32int lba, u32int size, Tentry *t)
385 {
386 	static int n;
387 	Dospart *p;
388 
389 	p = emalloc(sizeof(*p));
390 	if(name)
391 		p->name = estrdup(name);
392 	else{
393 		p->name = emalloc(20);
394 		sprint(p->name, "%c%d", primary ? 'p' : 's', ++n);
395 	}
396 
397 	if(t)
398 		p->Tentry = *t;
399 	else
400 		memset(&p->Tentry, 0, sizeof(Tentry));
401 
402 	p->changed = 0;
403 	p->start = lba/sec2cyl;
404 	p->end = (lba+size)/sec2cyl;
405 	p->ctlstart = lba;
406 	p->ctlend = lba+size;
407 	p->lba = lba;
408 	p->size = size;
409 	p->primary = primary;
410 	return p;
411 }
412 
413 /*
414  * Recovery takes care of remembering what the various tables
415  * looked like when we started, attempting to restore them when
416  * we are finished.
417  */
418 static Recover	*rtab;
419 static int		nrtab;
420 
421 static void
422 addrecover(Table t, ulong lba)
423 {
424 	if((nrtab%8) == 0) {
425 		rtab = realloc(rtab, (nrtab+8)*sizeof(rtab[0]));
426 		if(rtab == nil)
427 			sysfatal("out of memory");
428 	}
429 	rtab[nrtab] = (Recover){t, lba};
430 	nrtab++;
431 }
432 
433 static void
434 recover(Edit *edit)
435 {
436 	int err, i, ctlfd;
437 	vlong offset;
438 
439 	err = 0;
440 	for(i=0; i<nrtab; i++)
441 		if(diskwrite(edit->disk, &rtab[i].table, sizeof(Table), rtab[i].lba, Toffset) < 0)
442 			err = 1;
443 	if(err) {
444 		fprint(2, "warning: some writes failed during restoration of old partition tables\n");
445 		exits("inconsistent");
446 	} else {
447 		fprint(2, "restored old partition tables\n");
448 	}
449 
450 	ctlfd = edit->disk->ctlfd;
451 	offset = edit->disk->offset;
452 	if(ctlfd >= 0){
453 		for(i=0; i<edit->npart; i++)
454 			if(edit->part[i]->ctlname && fprint(ctlfd, "delpart %s", edit->part[i]->ctlname)<0)
455 				fprint(2, "delpart failed: %s: %r", edit->part[i]->ctlname);
456 		for(i=0; i<edit->nctlpart; i++)
457 			if(edit->part[i]->name && fprint(ctlfd, "delpart %s", edit->ctlpart[i]->name)<0)
458 				fprint(2, "delpart failed: %s: %r", edit->ctlpart[i]->name);
459 		for(i=0; i<edit->nctlpart; i++){
460 			if(fprint(ctlfd, "part %s %lld %lld", edit->ctlpart[i]->name,
461 				edit->ctlpart[i]->start+offset, edit->ctlpart[i]->end+offset) < 0){
462 				fprint(2, "restored disk partition table but not kernel; reboot\n");
463 				exits("inconsistent");
464 			}
465 		}
466 	}
467 	exits("restored");
468 
469 }
470 
471 /*
472  * Read the partition table (including extended partition tables)
473  * from the disk into the part array.
474  */
475 static void
476 rdpart(Edit *edit, ulong lba, ulong xbase)
477 {
478 	char *err;
479 	Table table;
480 	Tentry *tp, *ep;
481 	Dospart *p;
482 
483 	if(xbase == 0)
484 		xbase = lba;
485 
486 	diskread(edit->disk, &table, sizeof table, mbroffset+lba, Toffset);
487 	addrecover(table, mbroffset+lba);
488 
489 	if(table.magic[0] != Magic0 || table.magic[1] != Magic1) {
490 		assert(lba != 0);
491 		return;
492 	}
493 
494 	for(tp=table.entry, ep=tp+NTentry; tp<ep && npart < Mpart; tp++) {
495 		switch(tp->type) {
496 		case TypeEMPTY:
497 			break;
498 		case TypeEXTENDED:
499 		case TypeEXTHUGE:
500 		case TypeLINUXEXT:
501 			rdpart(edit, xbase+getle32(tp->xlba), xbase);
502 			break;
503 		default:
504 			p = mkpart(nil, lba==0, lba+getle32(tp->xlba), getle32(tp->xsize), tp);
505 			if(err = addpart(edit, p))
506 				fprint(2, "adding partition: %s\n", err);
507 			break;
508 		}
509 	}
510 }
511 
512 static void
513 blankpart(Edit *edit)
514 {
515 	edit->changed = 1;
516 }
517 
518 static void
519 findmbr(Edit *edit)
520 {
521 	Table table;
522 	Tentry *tp;
523 
524 	diskread(edit->disk, &table, sizeof(Table), 0, Toffset);
525 	if(table.magic[0] != Magic0 || table.magic[1] != Magic1)
526 		sysfatal("did not find master boot record");
527 
528 	for(tp = table.entry; tp < &table.entry[NTentry]; tp++)
529 		if(tp->type == TypeDMDDO)
530 			mbroffset = edit->disk->s;
531 }
532 
533 static int
534 haveroom(Edit *edit, int primary, vlong start)
535 {
536 	int i, lastsec, n;
537 	Dospart *p, *q;
538 	ulong pend, qstart;
539 
540 	if(primary) {
541 		/*
542 		 * must be open primary slot.
543 		 * primary slots are taken by primary partitions
544 		 * and runs of secondary partitions.
545 		 */
546 		n = 0;
547 		lastsec = 0;
548 		for(i=0; i<edit->npart; i++) {
549 			p = (Dospart*)edit->part[i];
550 			if(p->primary)
551 				n++, lastsec=0;
552 			else if(!lastsec)
553 				n++, lastsec=1;
554 		}
555 		return n<4;
556 	}
557 
558 	/*
559 	 * secondary partitions can be inserted between two primary
560 	 * partitions only if there is an empty primary slot.
561 	 * otherwise, we can put a new secondary partition next
562 	 * to a secondary partition no problem.
563 	 */
564 	n = 0;
565 	for(i=0; i<edit->npart; i++){
566 		p = (Dospart*)edit->part[i];
567 		if(p->primary)
568 			n++;
569 		pend = p->end;
570 		if(i+1<edit->npart){
571 			q = (Dospart*)edit->part[i+1];
572 			qstart = q->start;
573 		}else{
574 			qstart = edit->end;
575 			q = nil;
576 		}
577 		if(start < pend || start >= qstart)
578 			continue;
579 		/* we go between these two */
580 		if(p->primary==0 || (q && q->primary==0))
581 			return 1;
582 	}
583 	/* not next to a secondary, need a new primary */
584 	return n<4;
585 }
586 
587 static void
588 autopart(Edit *edit)
589 {
590 	char *err;
591 	int active, i;
592 	vlong bigstart, bigsize, start;
593 	Dospart *p;
594 
595 	for(i=0; i<edit->npart; i++)
596 		if(((Dospart*)edit->part[i])->type == Type9)
597 			return;
598 
599 	/* look for the biggest gap in which we can put a primary partition */
600 	start = 0;
601 	bigsize = 0;
602 	SET(bigstart);
603 	for(i=0; i<edit->npart; i++) {
604 		p = (Dospart*)edit->part[i];
605 		if(p->start > start && p->start - start > bigsize && haveroom(edit, 1, start)) {
606 			bigsize = p->start - start;
607 			bigstart = start;
608 		}
609 		start = p->end;
610 	}
611 
612 	if(edit->end - start > bigsize && haveroom(edit, 1, start)) {
613 		bigsize = edit->end - start;
614 		bigstart = start;
615 	}
616 	if(bigsize < 1) {
617 		fprint(2, "couldn't find space or partition slot for plan 9 partition\n");
618 		return;
619 	}
620 
621 	/* set new partition active only if no others are */
622 	active = Active;
623 	for(i=0; i<edit->npart; i++)
624 		if(((Dospart*)edit->part[i])->primary && (((Dospart*)edit->part[i])->active & Active))
625 			active = 0;
626 
627 	/* add new plan 9 partition */
628 	bigsize *= sec2cyl;
629 	bigstart *= sec2cyl;
630 	if(bigstart == 0) {
631 		bigstart += edit->disk->s;
632 		bigsize -= edit->disk->s;
633 	}
634 	p = mkpart(nil, 1, bigstart, bigsize, nil);
635 	p->active = active;
636 	p->changed = 1;
637 	p->type = Type9;
638 	edit->changed = 1;
639 	if(err = addpart(edit, p)) {
640 		fprint(2, "error adding plan9 partition: %s\n", err);
641 		return;
642 	}
643 }
644 
645 typedef struct Name Name;
646 struct Name {
647 	char *name;
648 	Name *link;
649 };
650 Name *namelist;
651 static void
652 plan9print(Dospart *part, int fd)
653 {
654 	int i, ok;
655 	char *name, *vname;
656 	Name *n;
657 	vlong start, end;
658 	char *sep;
659 
660 	vname = types[part->type].name;
661 	if(vname==nil || strcmp(vname, "")==0) {
662 		part->ctlname = "";
663 		return;
664 	}
665 
666 	start = mbroffset+part->lba;
667 	end = start+part->size;
668 
669 	/* avoid names like plan90 */
670 	i = strlen(vname) - 1;
671 	if(vname[i] >= '0' && vname[i] <= '9')
672 		sep = ".";
673 	else
674 		sep = "";
675 
676 	i = 0;
677 	name = emalloc(strlen(vname)+10);
678 
679 	sprint(name, "%s", vname);
680 	do {
681 		ok = 1;
682 		for(n=namelist; n; n=n->link) {
683 			if(strcmp(name, n->name) == 0) {
684 				i++;
685 				sprint(name, "%s%s%d", vname, sep, i);
686 				ok = 0;
687 			}
688 		}
689 	} while(ok == 0);
690 
691 	n = emalloc(sizeof(*n));
692 	n->name = name;
693 	n->link = namelist;
694 	namelist = n;
695 	part->ctlname = name;
696 
697 	if(fd >= 0)
698 		print("part %s %lld %lld\n", name, start, end);
699 }
700 
701 static void
702 freenamelist(void)
703 {
704 	Name *n, *next;
705 
706 	for(n=namelist; n; n=next) {
707 		next = n->link;
708 		free(n);
709 	}
710 	namelist = nil;
711 }
712 
713 static void
714 cmdprintctl(Edit *edit, int ctlfd)
715 {
716 	int i;
717 
718 	freenamelist();
719 	for(i=0; i<edit->npart; i++)
720 		plan9print((Dospart*)edit->part[i], -1);
721 	ctldiff(edit, ctlfd);
722 }
723 
724 static char*
725 cmdokname(Edit*, char *name)
726 {
727 	char *q;
728 
729 	if(name[0] != 'p' && name[0] != 's')
730 		return "name must be pN or sN";
731 
732 	strtol(name+1, &q, 10);
733 	if(*q != '\0')
734 		return "name must be pN or sN";
735 
736 	return nil;
737 }
738 
739 #define GB (1024*1024*1024)
740 #define MB (1024*1024)
741 #define KB (1024)
742 
743 static void
744 cmdsum(Edit *edit, Part *vp, vlong a, vlong b)
745 {
746 	char *name, *ty;
747 	char buf[3];
748 	char *suf;
749 	Dospart *p;
750 	vlong sz, div;
751 
752 	p = (Dospart*)vp;
753 
754 	buf[0] = p && p->changed ? '\'' : ' ';
755 	buf[1] = p && (p->active & Active) ? '*' : ' ';
756 	buf[2] = '\0';
757 
758 	name = p ? p->name : "empty";
759 	ty = p ? typestr0(p->type) : "";
760 
761 	sz = (b-a)*edit->disk->secsize*sec2cyl;
762 	if(sz >= 1*GB){
763 		suf = "GB";
764 		div = GB;
765 	}else if(sz >= 1*MB){
766 		suf = "MB";
767 		div = MB;
768 	}else if(sz >= 1*KB){
769 		suf = "KB";
770 		div = KB;
771 	}else{
772 		suf = "B ";
773 		div = 1;
774 	}
775 
776 	if(div == 1)
777 		print("%s %-12s %*lld %-*lld (%lld cylinders, %lld %s) %s\n", buf, name,
778 			edit->disk->width, a, edit->disk->width, b, b-a, sz, suf, ty);
779 	else
780 		print("%s %-12s %*lld %-*lld (%lld cylinders, %lld.%.2d %s) %s\n", buf, name,
781 			edit->disk->width, a, edit->disk->width, b,  b-a,
782 			sz/div, (int)(((sz%div)*100)/div), suf, ty);
783 }
784 
785 static char*
786 cmdadd(Edit *edit, char *name, vlong start, vlong end)
787 {
788 	Dospart *p;
789 
790 	if(!haveroom(edit, name[0]=='p', start))
791 		return "no room for partition";
792 	start *= sec2cyl;
793 	end *= sec2cyl;
794 	if(start == 0 || name[0] != 'p')
795 		start += edit->disk->s;
796 	p = mkpart(name, name[0]=='p', start, end-start, nil);
797 	p->changed = 1;
798 	p->type = Type9;
799 	return addpart(edit, p);
800 }
801 
802 static char*
803 cmddel(Edit *edit, Part *p)
804 {
805 	return delpart(edit, p);
806 }
807 
808 static char*
809 cmdwrite(Edit *edit)
810 {
811 	wrpart(edit);
812 	return nil;
813 }
814 
815 static char *help =
816 	"A name - set partition active\n"
817 	"P - print table in ctl format\n"
818 	"R - restore disk back to initial configuration and exit\n"
819 	"e - show empty dos partitions\n"
820 	"t name [type] - set partition type\n";
821 
822 static char*
823 cmdhelp(Edit*)
824 {
825 	print("%s\n", help);
826 	return nil;
827 }
828 
829 static char*
830 cmdactive(Edit *edit, int nf, char **f)
831 {
832 	int i;
833 	Dospart *p, *ip;
834 
835 	if(nf != 2)
836 		return "args";
837 
838 	if(f[1][0] != 'p')
839 		return "cannot set secondary partition active";
840 
841 	if((p = (Dospart*)findpart(edit, f[1])) == nil)
842 		return "unknown partition";
843 
844 	for(i=0; i<edit->npart; i++) {
845 		ip = (Dospart*)edit->part[i];
846 		if(ip->active & Active) {
847 			ip->active &= ~Active;
848 			ip->changed = 1;
849 			edit->changed = 1;
850 		}
851 	}
852 
853 	if((p->active & Active) == 0) {
854 		p->active |= Active;
855 		p->changed = 1;
856 		edit->changed = 1;
857 	}
858 
859 	return nil;
860 }
861 
862 static char*
863 strupr(char *s)
864 {
865 	char *p;
866 
867 	for(p=s; *p; p++)
868 		*p = toupper(*p);
869 	return s;
870 }
871 
872 static void
873 dumplist(void)
874 {
875 	int i, n;
876 
877 	n = 0;
878 	for(i=0; i<256; i++) {
879 		if(types[i].desc) {
880 			print("%-16s", types[i].desc);
881 			if(n++%4 == 3)
882 				print("\n");
883 		}
884 	}
885 	if(n%4)
886 		print("\n");
887 }
888 
889 static char*
890 cmdtype(Edit *edit, int nf, char **f)
891 {
892 	char *q;
893 	Dospart *p;
894 	int i;
895 
896 	if(nf < 2)
897 		return "args";
898 
899 	if((p = (Dospart*)findpart(edit, f[1])) == nil)
900 		return "unknown partition";
901 
902 	if(nf == 2) {
903 		for(;;) {
904 			fprint(2, "new partition type [? for list]: ");
905 			q = getline(edit);
906 			if(q[0] == '?')
907 				dumplist();
908 			else
909 				break;
910 		}
911 	} else
912 		q = f[2];
913 
914 	strupr(q);
915 	for(i=0; i<256; i++)
916 		if(types[i].desc && strcmp(types[i].desc, q) == 0)
917 			break;
918 	if(i < 256 && p->type != i) {
919 		p->type = i;
920 		p->changed = 1;
921 		edit->changed = 1;
922 	}
923 	return nil;
924 }
925 
926 static char*
927 cmdext(Edit *edit, int nf, char **f)
928 {
929 	switch(f[0][0]) {
930 	case 'A':
931 		return cmdactive(edit, nf, f);
932 	case 't':
933 		return cmdtype(edit, nf, f);
934 	case 'R':
935 		recover(edit);
936 		return nil;
937 	default:
938 		return "unknown command";
939 	}
940 }
941 
942 static int
943 Dfmt(Fmt *f)
944 {
945 	char buf[60];
946 	uchar *p;
947 	int c, h, s;
948 
949 	p = va_arg(f->args, uchar*);
950 	h = p[0];
951 	c = p[2];
952 	c |= (p[1]&0xC0)<<2;
953 	s = (p[1] & 0x3F);
954 
955 	sprint(buf, "%d/%d/%d", c, h, s);
956 	return fmtstrcpy(f, buf);
957 }
958 
959 static void
960 writechs(Disk *disk, uchar *p, vlong lba)
961 {
962 	int c, h, s;
963 
964 	s = lba % disk->s;
965 	h = (lba / disk->s) % disk->h;
966 	c = lba / (disk->s * disk->h);
967 
968 	if(c >= 1024) {
969 		c = 1023;
970 		h = disk->h - 1;
971 		s = disk->s - 1;
972 	}
973 
974 	p[0] = h;
975 	p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0);
976 	p[2] = c;
977 }
978 
979 static void
980 wrtentry(Disk *disk, Tentry *tp, int type, u32int xbase, u32int lba, u32int end)
981 {
982 	tp->type = type;
983 	writechs(disk, &tp->starth, lba);
984 	writechs(disk, &tp->endh, end-1);
985 	putle32(tp->xlba, lba-xbase);
986 	putle32(tp->xsize, end-lba);
987 }
988 
989 static int
990 wrextend(Edit *edit, int i, vlong xbase, vlong startlba, vlong *endlba)
991 {
992 	int ni;
993 	Table table;
994 	Tentry *tp, *ep;
995 	Dospart *p;
996 	Disk *disk;
997 
998 	if(i == edit->npart){
999 		*endlba = edit->disk->secs;
1000 	Finish:
1001 		if(startlba < *endlba){
1002 			disk = edit->disk;
1003 			diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset);
1004 			tp = table.entry;
1005 			ep = tp+NTentry;
1006 			for(; tp<ep; tp++)
1007 				memset(tp, 0, sizeof *tp);
1008 			table.magic[0] = Magic0;
1009 			table.magic[1] = Magic1;
1010 
1011 			if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0)
1012 				recover(edit);
1013 		}
1014 		return i;
1015 	}
1016 
1017 	p = (Dospart*)edit->part[i];
1018 	if(p->primary){
1019 		*endlba = (vlong)p->start*sec2cyl;
1020 		goto Finish;
1021 	}
1022 
1023 	disk = edit->disk;
1024 	diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset);
1025 	tp = table.entry;
1026 	ep = tp+NTentry;
1027 
1028 	ni = wrextend(edit, i+1, xbase, p->end*sec2cyl, endlba);
1029 
1030 	*tp = p->Tentry;
1031 	wrtentry(disk, tp, p->type, startlba, startlba+disk->s, p->end*sec2cyl);
1032 	tp++;
1033 
1034 	if(p->end*sec2cyl != *endlba){
1035 		memset(tp, 0, sizeof *tp);
1036 		wrtentry(disk, tp, TypeEXTENDED, xbase, p->end*sec2cyl, *endlba);
1037 		tp++;
1038 	}
1039 
1040 	for(; tp<ep; tp++)
1041 		memset(tp, 0, sizeof *tp);
1042 
1043 	table.magic[0] = Magic0;
1044 	table.magic[1] = Magic1;
1045 
1046 	if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0)
1047 		recover(edit);
1048 	return ni;
1049 }
1050 
1051 static void
1052 wrpart(Edit *edit)
1053 {
1054 	int i, ni, t;
1055 	Table table;
1056 	Tentry *tp, *ep;
1057 	Disk *disk;
1058 	vlong s, endlba;
1059 	Dospart *p;
1060 
1061 	disk = edit->disk;
1062 
1063 	diskread(disk, &table, sizeof table, mbroffset, Toffset);
1064 
1065 	tp = table.entry;
1066 	ep = tp+NTentry;
1067 	for(i=0; i<edit->npart && tp<ep; ) {
1068 		p = (Dospart*)edit->part[i];
1069 		if(p->start == 0)
1070 			s = disk->s;
1071 		else
1072 			s = p->start*sec2cyl;
1073 		if(p->primary) {
1074 			*tp = p->Tentry;
1075 			wrtentry(disk, tp, p->type, 0, s, p->end*sec2cyl);
1076 			tp++;
1077 			i++;
1078 		} else {
1079 			ni = wrextend(edit, i, p->start*sec2cyl, p->start*sec2cyl, &endlba);
1080 			memset(tp, 0, sizeof *tp);
1081 			if(endlba >= 1024*sec2cyl)
1082 				t = TypeEXTHUGE;
1083 			else
1084 				t = TypeEXTENDED;
1085 			wrtentry(disk, tp, t, 0, s, endlba);
1086 			tp++;
1087 			i = ni;
1088 		}
1089 	}
1090 	for(; tp<ep; tp++)
1091 		memset(tp, 0, sizeof(*tp));
1092 
1093 	if(i != edit->npart)
1094 		sysfatal("cannot happen #1");
1095 
1096 	if(diskwrite(disk, &table, sizeof table, mbroffset, Toffset) < 0)
1097 		recover(edit);
1098 
1099 	/* bring parts up to date */
1100 	freenamelist();
1101 	for(i=0; i<edit->npart; i++)
1102 		plan9print((Dospart*)edit->part[i], -1);
1103 
1104 	if(ctldiff(edit, disk->ctlfd) < 0)
1105 		fprint(2, "?warning: partitions could not be updated in devsd\n");
1106 }
1107