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