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