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