jehanne/sys/src/cmd/disk/prep/fdisk.c

1139 lines
23 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* fdisk - edit dos disk partition table
*/
#include <u.h>
#include <lib9.h>
#include <bio.h>
#include <jehanne/ctype.h>
#include <disk.h>
#include "edit.h"
typedef struct Dospart Dospart;
enum {
NTentry = 4,
Mpart = 64,
};
static void rdpart(Edit*, uint64_t, uint64_t);
static void findmbr(Edit*);
static void autopart(Edit*);
static void wrpart(Edit*);
static void blankpart(Edit*);
static void recover(Edit*);
static int Dfmt(Fmt*);
static int blank;
static int dowrite;
static int file;
static int rdonly;
static int doauto;
static int64_t mbroffset;
static int printflag;
static int printchs;
static int sec2cyl;
static int written;
static void cmdsum(Edit*, Part*, int64_t, int64_t);
static char *cmdadd(Edit*, char*, int64_t, int64_t);
static char *cmddel(Edit*, Part*);
static char *cmdext(Edit*, int, char**);
static char *cmdhelp(Edit*);
static char *cmdokname(Edit*, char*);
static char *cmdwrite(Edit*);
static void cmdprintctl(Edit*, int);
#pragma varargck type "D" uchar*
Edit edit = {
.add= cmdadd,
.del= cmddel,
.ext= cmdext,
.help= cmdhelp,
.okname= cmdokname,
.sum= cmdsum,
.write= cmdwrite,
.printctl= cmdprintctl,
.unit= "cylinder",
};
/*
* Catch the obvious error routines to fix up the disk.
*/
void
sysfatal(const char *fmt, ...)
{
char buf[1024];
va_list arg;
va_start(arg, fmt);
vseprint(buf, buf+sizeof(buf), fmt, arg);
va_end(arg);
if(argv0)
fprint(2, "%s: %s\n", argv0, buf);
else
fprint(2, "%s\n", buf);
if(written)
recover(&edit);
exits(buf);
}
void
recoverOnAbort(void)
{
fprint(2, "abort\n");
recover(&edit);
}
void
usage(void)
{
fprint(2, "usage: disk/fdisk [-abfprvw] [-s sectorsize] /dev/sdC0/data\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int64_t secsize;
_abort = recoverOnAbort;
secsize = 0;
ARGBEGIN{
case 'a':
doauto++;
break;
case 'b':
blank++;
break;
case 'f':
file++;
break;
case 'p':
printflag++;
break;
case 'r':
rdonly++;
break;
case 's':
secsize = atoi(ARGF());
break;
case 'v':
printchs++;
break;
case 'w':
dowrite++;
break;
}ARGEND;
fmtinstall('D', Dfmt);
if(argc != 1)
usage();
edit.disk = opendisk(argv[0], rdonly, file);
if(edit.disk == nil) {
fprint(2, "cannot open disk: %r\n");
exits("opendisk");
}
if(secsize != 0) {
edit.disk->secsize = secsize;
edit.disk->secs = edit.disk->size / secsize;
}
sec2cyl = edit.disk->h * edit.disk->s;
edit.end = edit.disk->secs / sec2cyl;
findmbr(&edit);
if(blank)
blankpart(&edit);
else
rdpart(&edit, 0, 0);
if(doauto)
autopart(&edit);
if(dowrite)
runcmd(&edit, (char[]){"w"});
if(printflag)
runcmd(&edit, (char[]){"P"});
if(dowrite || printflag)
exits(0);
fprint(2, "cylinder = %lld bytes\n", sec2cyl*edit.disk->secsize);
runcmd(&edit, (char[]){"p"});
for(;;) {
fprint(2, ">>> ");
runcmd(&edit, getline(&edit));
}
}
typedef struct Tentry Tentry;
typedef struct Table Table;
typedef struct Type Type;
typedef struct Tab Tab;
typedef struct Recover Recover;
struct Tentry {
uint8_t active; /* active flag */
uint8_t starth; /* starting head */
uint8_t starts; /* starting sector */
uint8_t startc; /* starting cylinder */
uint8_t type; /* partition type */
uint8_t endh; /* ending head */
uint8_t ends; /* ending sector */
uint8_t endc; /* ending cylinder */
uint8_t xlba[4]; /* starting LBA from beginning of disc or ext. partition */
uint8_t xsize[4]; /* size in sectors */
};
struct Table {
Tentry entry[NTentry];
uint8_t magic[2];
uint8_t size[];
};
enum {
Active = 0x80, /* partition is active */
Primary = 0x01, /* internal flag */
TypeBB = 0xFF,
TypeEMPTY = 0x00,
TypeFAT12 = 0x01,
TypeXENIX = 0x02, /* root */
TypeXENIXUSR = 0x03, /* usr */
TypeFAT16 = 0x04,
TypeEXTENDED = 0x05,
TypeFATHUGE = 0x06,
TypeHPFS = 0x07,
TypeAIXBOOT = 0x08,
TypeAIXDATA = 0x09,
TypeOS2BOOT = 0x0A, /* OS/2 Boot Manager */
TypeFAT32 = 0x0B, /* FAT 32 */
TypeFAT32LBA = 0x0C, /* FAT 32 needing LBA support */
TypeFAT16X = 0x0E, /* FAT 16 needing LBA support */
TypeEXTHUGE = 0x0F, /* FAT 32 extended partition */
TypeUNFORMATTED = 0x16, /* unformatted primary partition (OS/2 FDISK)? */
TypeHPFS2 = 0x17,
TypeIBMRecovery = 0x1C, /* really hidden fat */
TypeCPM0 = 0x52,
TypeDMDDO = 0x54, /* Disk Manager Dynamic Disk Overlay */
TypeGB = 0x56, /* ???? */
TypeSPEEDSTOR = 0x61,
TypeSYSV386 = 0x63, /* also HURD? */
TypeNETWARE = 0x64,
TypePCIX = 0x75,
TypeMINIX13 = 0x80, /* Minix v1.3 and below */
TypeMINIX = 0x81, /* Minix v1.5+ */
TypeLINUXSWAP = 0x82,
TypeLINUX = 0x83,
TypeLINUXEXT = 0x85,
TypeLINUXLVM = 0x8E, /* logical volume manager */
TypeAMOEBA = 0x93,
TypeAMOEBABB = 0x94,
TypeBSD386 = 0xA5,
TypeNETBSD = 0xA9,
TypeBSDI = 0xB7,
TypeBSDISWAP = 0xB8,
TypeOTHER = 0xDA,
TypeCPM = 0xDB,
TypeDellRecovery= 0xDE,
TypeSPEEDSTOR12 = 0xE1,
TypeSPEEDSTOR16 = 0xE4,
TypeEFIProtect = 0xEE,
TypeEFI = 0xEF,
TypeLANSTEP = 0xFE,
Type9 = 0x39,
Toffset = 446, /* offset of partition table in sector */
Magic0 = 0x55,
Magic1 = 0xAA,
Tablesz = offsetof(Table, size[0]),
};
struct Type {
char *desc;
char *name;
};
struct Dospart {
Part;
Tentry;
uint32_t lba;
uint32_t size;
int primary;
};
struct Recover {
Table table;
uint32_t lba;
};
static Type types[256] = {
[TypeEMPTY] { "EMPTY", "" },
[TypeFAT12] { "FAT12", "dos" },
[TypeFAT16] { "FAT16", "dos" },
[TypeFAT32] { "FAT32", "dos" },
[TypeFAT32LBA] { "FAT32LBA", "dos" },
[TypeFAT16X] { "FAT16X", "dos" },
[TypeEXTHUGE] { "EXTHUGE", "" },
[TypeIBMRecovery] { "IBMRECOVERY", "ibm" },
[TypeEXTENDED] { "EXTENDED", "" },
[TypeFATHUGE] { "FATHUGE", "dos" },
[TypeBB] { "BB", "bb" },
[TypeXENIX] { "XENIX", "xenix" },
[TypeXENIXUSR] { "XENIX USR", "xenixusr" },
[TypeHPFS] { "HPFS", "ntfs" },
[TypeAIXBOOT] { "AIXBOOT", "aixboot" },
[TypeAIXDATA] { "AIXDATA", "aixdata" },
[TypeOS2BOOT] { "OS/2BOOT", "os2boot" },
[TypeUNFORMATTED] { "UNFORMATTED", "" },
[TypeHPFS2] { "HPFS2", "hpfs2" },
[TypeCPM0] { "CPM0", "cpm0" },
[TypeDMDDO] { "DMDDO", "dmdd0" },
[TypeGB] { "GB", "gb" },
[TypeSPEEDSTOR] { "SPEEDSTOR", "speedstor" },
[TypeSYSV386] { "SYSV386", "sysv386" },
[TypeNETWARE] { "NETWARE", "netware" },
[TypePCIX] { "PCIX", "pcix" },
[TypeMINIX13] { "MINIXV1.3", "minix13" },
[TypeMINIX] { "MINIXV1.5", "minix15" },
[TypeLINUXSWAP] { "LINUXSWAP", "linuxswap" },
[TypeLINUX] { "LINUX", "linux" },
[TypeLINUXEXT] { "LINUXEXTENDED", "" },
[TypeLINUXLVM] { "LINUXLVM", "linuxlvm" },
[TypeAMOEBA] { "AMOEBA", "amoeba" },
[TypeAMOEBABB] { "AMOEBABB", "amoebaboot" },
[TypeBSD386] { "BSD386", "bsd386" },
[TypeNETBSD] { "NETBSD", "netbsd" },
[TypeBSDI] { "BSDI", "bsdi" },
[TypeBSDISWAP] { "BSDISWAP", "bsdiswap" },
[TypeOTHER] { "OTHER", "other" },
[TypeCPM] { "CPM", "cpm" },
[TypeDellRecovery] { "DELLRECOVERY", "dell" },
[TypeSPEEDSTOR12] { "SPEEDSTOR12", "speedstor" },
[TypeSPEEDSTOR16] { "SPEEDSTOR16", "speedstor" },
[TypeEFIProtect] { "EFIPROTECT", "efiprotect" },
[TypeEFI] { "EFI", "efi" },
[TypeLANSTEP] { "LANSTEP", "lanstep" },
[Type9] { "PLAN9", "plan9" },
};
static int npart;
static char*
typestr0(int type)
{
static char buf[100];
sprint(buf, "type %d", type);
if(type < 0 || type >= 256)
return buf;
if(types[type].desc == nil)
return buf;
return types[type].desc;
}
static uint32_t
getle32(void* v)
{
uint8_t *p;
p = v;
return (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0];
}
static void
putle32(void* v, uint32_t i)
{
uint8_t *p;
p = v;
p[0] = i;
p[1] = i>>8;
p[2] = i>>16;
p[3] = i>>24;
}
static void
diskread(Disk *disk, void *data, int ndata, uint32_t sec, uint32_t off)
{
if(sys_seek(disk->fd, (int64_t)sec*disk->secsize+off, 0) != (int64_t)sec*disk->secsize+off)
sysfatal("diskread seek %lud.%lud: %r", (uint32_t)sec,
(uint32_t)off);
if(readn(disk->fd, data, ndata) != ndata)
sysfatal("diskread %lud at %lud.%lud: %r", (uint32_t)ndata,
(uint32_t)sec, (uint32_t)off);
}
static int
diskwrite(Disk *disk, void *data, int ndata, uint32_t sec, uint32_t off)
{
written = 1;
if(sys_seek(disk->wfd, (int64_t)sec*disk->secsize+off, 0) != (int64_t)sec*disk->secsize+off)
goto Error;
if(jehanne_write(disk->wfd, data, ndata) != ndata)
goto Error;
return 0;
Error:
fprint(2, "write %d bytes at %lud.%lud failed: %r\n", ndata,
(uint32_t)sec, (uint32_t)off);
return -1;
}
static Dospart*
mkpart(char *name, int primary, int64_t lba, int64_t size, Tentry *t)
{
static int n;
Dospart *p;
p = emalloc(sizeof(*p));
if(name)
p->name = estrdup(name);
else{
p->name = emalloc(20);
sprint(p->name, "%c%d", primary ? 'p' : 's', ++n);
}
if(t)
p->Tentry = *t;
else
memset(&p->Tentry, 0, sizeof(Tentry));
p->changed = 0;
p->start = lba/sec2cyl;
p->end = (lba+size)/sec2cyl;
p->ctlstart = lba;
p->ctlend = lba+size;
p->lba = lba;
if (p->lba != lba)
fprint(2, "%s: start of partition (%lld) won't fit in MBR table\n", argv0, lba);
p->size = size;
if (p->size != size)
fprint(2, "%s: size of partition (%lld) won't fit in MBR table\n", argv0, size);
p->primary = primary;
return p;
}
/*
* Recovery takes care of remembering what the various tables
* looked like when we started, attempting to restore them when
* we are finished.
*/
static Recover *rtab;
static int nrtab;
static void
addrecover(Table t, uint32_t lba)
{
if((nrtab%8) == 0) {
rtab = realloc(rtab, (nrtab+8)*sizeof(rtab[0]));
if(rtab == nil)
sysfatal("out of memory");
}
rtab[nrtab] = (Recover){t, lba};
nrtab++;
}
static void
recover(Edit *edit)
{
int err, i, ctlfd;
int64_t offset;
err = 0;
for(i=0; i<nrtab; i++)
if(diskwrite(edit->disk, &rtab[i].table, Tablesz, rtab[i].lba, Toffset) < 0)
err = 1;
if(err) {
fprint(2, "warning: some writes failed during restoration of old partition tables\n");
exits("inconsistent");
} else {
fprint(2, "restored old partition tables\n");
}
ctlfd = edit->disk->ctlfd;
offset = edit->disk->offset;
if(ctlfd >= 0){
for(i=0; i<edit->npart; i++)
if(edit->part[i]->ctlname && fprint(ctlfd, "delpart %s", edit->part[i]->ctlname)<0)
fprint(2, "delpart failed: %s: %r", edit->part[i]->ctlname);
for(i=0; i<edit->nctlpart; i++)
if(edit->part[i]->name && fprint(ctlfd, "delpart %s", edit->ctlpart[i]->name)<0)
fprint(2, "delpart failed: %s: %r", edit->ctlpart[i]->name);
for(i=0; i<edit->nctlpart; i++){
if(fprint(ctlfd, "part %s %lld %lld", edit->ctlpart[i]->name,
edit->ctlpart[i]->start+offset, edit->ctlpart[i]->end+offset) < 0){
fprint(2, "restored disk partition table but not kernel; reboot\n");
exits("inconsistent");
}
}
}
exits("restored");
}
/*
* Read the partition table (including extended partition tables)
* from the disk into the part array.
*/
static void
rdpart(Edit *edit, uint64_t lba, uint64_t xbase)
{
char *err;
Table table;
Tentry *tp, *ep;
Dospart *p;
if(xbase == 0)
xbase = lba;
diskread(edit->disk, &table, Tablesz, mbroffset+lba, Toffset);
addrecover(table, mbroffset+lba);
if(table.magic[0] != Magic0 || table.magic[1] != Magic1) {
assert(lba != 0);
return;
}
for(tp=table.entry, ep=tp+NTentry; tp<ep && npart < Mpart; tp++) {
switch(tp->type) {
case TypeEMPTY:
break;
case TypeEXTENDED:
case TypeEXTHUGE:
case TypeLINUXEXT:
rdpart(edit, xbase+getle32(tp->xlba), xbase);
break;
default:
p = mkpart(nil, lba==0, lba+getle32(tp->xlba), getle32(tp->xsize), tp);
if(err = addpart(edit, p))
fprint(2, "adding partition: %s\n", err);
break;
}
}
}
static void
blankpart(Edit *edit)
{
edit->changed = 1;
}
static void
findmbr(Edit *edit)
{
Table table;
Tentry *tp;
diskread(edit->disk, &table, Tablesz, 0, Toffset);
if(table.magic[0] != Magic0 || table.magic[1] != Magic1)
sysfatal("did not find master boot record");
for(tp = table.entry; tp < &table.entry[NTentry]; tp++)
if(tp->type == TypeDMDDO)
mbroffset = edit->disk->s;
}
static int
haveroom(Edit *edit, int primary, int64_t start)
{
int i, lastsec, n;
Dospart *p, *q;
uint32_t pend, qstart;
if(primary) {
/*
* must be open primary slot.
* primary slots are taken by primary partitions
* and runs of secondary partitions.
*/
n = 0;
lastsec = 0;
for(i=0; i<edit->npart; i++) {
p = (Dospart*)edit->part[i];
if(p->primary)
n++, lastsec=0;
else if(!lastsec)
n++, lastsec=1;
}
return n<4;
}
/*
* secondary partitions can be inserted between two primary
* partitions only if there is an empty primary slot.
* otherwise, we can put a new secondary partition next
* to a secondary partition no problem.
*/
n = 0;
for(i=0; i<edit->npart; i++){
p = (Dospart*)edit->part[i];
if(p->primary)
n++;
pend = p->end;
if(i+1<edit->npart){
q = (Dospart*)edit->part[i+1];
qstart = q->start;
}else{
qstart = edit->end;
q = nil;
}
if(start < pend || start >= qstart)
continue;
/* we go between these two */
if(p->primary==0 || (q && q->primary==0))
return 1;
}
/* not next to a secondary, need a new primary */
return n<4;
}
static void
autopart(Edit *edit)
{
char *err;
int active, i;
int64_t bigstart, bigsize, start;
Dospart *p;
for(i=0; i<edit->npart; i++)
if(((Dospart*)edit->part[i])->type == Type9)
return;
/* look for the biggest gap in which we can put a primary partition */
start = 0;
bigsize = 0;
SET(bigstart);
for(i=0; i<edit->npart; i++) {
p = (Dospart*)edit->part[i];
if(p->start > start && p->start - start > bigsize && haveroom(edit, 1, start)) {
bigsize = p->start - start;
bigstart = start;
}
start = p->end;
}
if(edit->end - start > bigsize && haveroom(edit, 1, start)) {
bigsize = edit->end - start;
bigstart = start;
}
if(bigsize < 1) {
fprint(2, "couldn't find space or partition slot for plan 9 partition\n");
return;
}
/* set new partition active only if no others are */
active = Active;
for(i=0; i<edit->npart; i++)
if(((Dospart*)edit->part[i])->primary && (((Dospart*)edit->part[i])->active & Active))
active = 0;
/* add new plan 9 partition */
bigsize *= sec2cyl;
bigstart *= sec2cyl;
if(bigstart == 0) {
bigstart += edit->disk->s;
bigsize -= edit->disk->s;
}
p = mkpart(nil, 1, bigstart, bigsize, nil);
p->active = active;
p->changed = 1;
p->type = Type9;
edit->changed = 1;
if(err = addpart(edit, p)) {
fprint(2, "error adding plan9 partition: %s\n", err);
return;
}
}
typedef struct Name Name;
struct Name {
char *name;
Name *link;
};
Name *namelist;
static void
plan9print(Dospart *part, int fd)
{
int i, ok;
char *name, *vname;
Name *n;
int64_t start, end;
char *sep;
vname = types[part->type].name;
if(vname==nil || strcmp(vname, "")==0) {
part->ctlname = "";
return;
}
start = mbroffset+part->lba;
end = start+part->size;
/* avoid names like plan90 */
i = strlen(vname) - 1;
if(vname[i] >= '0' && vname[i] <= '9')
sep = ".";
else
sep = "";
i = 0;
name = emalloc(strlen(vname)+10);
sprint(name, "%s", vname);
do {
ok = 1;
for(n=namelist; n; n=n->link) {
if(strcmp(name, n->name) == 0) {
i++;
sprint(name, "%s%s%d", vname, sep, i);
ok = 0;
}
}
} while(ok == 0);
n = emalloc(sizeof(*n));
n->name = name;
n->link = namelist;
namelist = n;
part->ctlname = name;
if(fd >= 0)
print("part %s %lld %lld\n", name, start, end);
}
static void
freenamelist(void)
{
Name *n, *next;
for(n=namelist; n; n=next) {
next = n->link;
free(n);
}
namelist = nil;
}
static void
cmdprintctl(Edit *edit, int ctlfd)
{
int i;
freenamelist();
for(i=0; i<edit->npart; i++)
plan9print((Dospart*)edit->part[i], -1);
ctldiff(edit, ctlfd);
}
static char*
cmdokname(Edit *e, char *name)
{
char *q;
if(name[0] != 'p' && name[0] != 's')
return "name must be pN or sN";
strtol(name+1, &q, 10);
if(*q != '\0')
return "name must be pN or sN";
return nil;
}
#define TB (1024LL*GB)
#define GB (1024*1024*1024)
#define MB (1024*1024)
#define KB (1024)
static void
cmdsum(Edit *edit, Part *vp, int64_t a, int64_t b)
{
char *name, *ty;
char buf[3];
char *suf;
Dospart *p;
int64_t sz, div;
p = (Dospart*)vp;
buf[0] = p && p->changed ? '\'' : ' ';
buf[1] = p && (p->active & Active) ? '*' : ' ';
buf[2] = '\0';
name = p ? p->name : "empty";
ty = p ? typestr0(p->type) : "";
sz = (b-a)*edit->disk->secsize*sec2cyl;
if(sz >= 1*TB){
suf = "TB";
div = TB;
}else if(sz >= 1*GB){
suf = "GB";
div = GB;
}else if(sz >= 1*MB){
suf = "MB";
div = MB;
}else if(sz >= 1*KB){
suf = "KB";
div = KB;
}else{
suf = "B ";
div = 1;
}
if(div == 1)
print("%s %-12s %*lld %-*lld (%lld cylinders, %lld %s) %s\n", buf, name,
edit->disk->width, a, edit->disk->width, b, b-a, sz, suf, ty);
else
print("%s %-12s %*lld %-*lld (%lld cylinders, %lld.%.2d %s) %s\n", buf, name,
edit->disk->width, a, edit->disk->width, b, b-a,
sz/div, (int)(((sz%div)*100)/div), suf, ty);
}
static char*
cmdadd(Edit *edit, char *name, int64_t start, int64_t end)
{
Dospart *p;
if(!haveroom(edit, name[0]=='p', start))
return "no room for partition";
start *= sec2cyl;
end *= sec2cyl;
if(start == 0 || name[0] != 'p')
start += edit->disk->s;
p = mkpart(name, name[0]=='p', start, end-start, nil);
p->changed = 1;
p->type = Type9;
return addpart(edit, p);
}
static char*
cmddel(Edit *edit, Part *p)
{
return delpart(edit, p);
}
static char*
cmdwrite(Edit *edit)
{
wrpart(edit);
return nil;
}
static char *help =
"A name - set partition active\n"
"P - print table in ctl format\n"
"R - restore disk back to initial configuration and exit\n"
"e - show empty dos partitions\n"
"t name [type] - set partition type\n";
static char*
cmdhelp(Edit *e)
{
print("%s\n", help);
return nil;
}
static char*
cmdactive(Edit *edit, int nf, char **f)
{
int i;
Dospart *p, *ip;
if(nf != 2)
return "args";
if(f[1][0] != 'p')
return "cannot set secondary partition active";
if((p = (Dospart*)findpart(edit, f[1])) == nil)
return "unknown partition";
for(i=0; i<edit->npart; i++) {
ip = (Dospart*)edit->part[i];
if(ip->active & Active) {
ip->active &= ~Active;
ip->changed = 1;
edit->changed = 1;
}
}
if((p->active & Active) == 0) {
p->active |= Active;
p->changed = 1;
edit->changed = 1;
}
return nil;
}
static char*
strupr(char *s)
{
char *p;
for(p=s; *p; p++)
*p = toupper(*p);
return s;
}
static void
dumplist(void)
{
int i, n;
n = 0;
for(i=0; i<256; i++) {
if(types[i].desc) {
print("%-16s", types[i].desc);
if(n++%4 == 3)
print("\n");
}
}
if(n%4)
print("\n");
}
static char*
cmdtype(Edit *edit, int nf, char **f)
{
char *q;
Dospart *p;
int i;
if(nf < 2)
return "args";
if((p = (Dospart*)findpart(edit, f[1])) == nil)
return "unknown partition";
if(nf == 2) {
for(;;) {
fprint(2, "new partition type [? for list]: ");
q = getline(edit);
if(q[0] == '?')
dumplist();
else
break;
}
} else
q = f[2];
strupr(q);
for(i=0; i<256; i++)
if(types[i].desc && strcmp(types[i].desc, q) == 0)
break;
if(i < 256 && p->type != i) {
p->type = i;
p->changed = 1;
edit->changed = 1;
}
return nil;
}
static char*
cmdext(Edit *edit, int nf, char **f)
{
switch(f[0][0]) {
case 'A':
return cmdactive(edit, nf, f);
case 't':
return cmdtype(edit, nf, f);
case 'R':
recover(edit);
return nil;
default:
return "unknown command";
}
}
static int
Dfmt(Fmt *f)
{
char buf[60];
uint8_t *p;
int c, h, s;
p = va_arg(f->args, uint8_t*);
h = p[0];
c = p[2];
c |= (p[1]&0xC0)<<2;
s = (p[1] & 0x3F);
sprint(buf, "%d/%d/%d", c, h, s);
return fmtstrcpy(f, buf);
}
static void
writechs(Disk *disk, uint8_t *p, int64_t lba)
{
int c, h, s;
s = lba % disk->s;
h = (lba / disk->s) % disk->h;
c = lba / (disk->s * disk->h);
if(c >= 1024) {
c = 1023;
h = disk->h - 1;
s = disk->s - 1;
}
p[0] = h;
p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0);
p[2] = c;
}
static void
wrtentry(Disk *disk, Tentry *tp, int type, uint32_t xbase, uint32_t lba,
uint32_t end)
{
tp->type = type;
writechs(disk, &tp->starth, lba);
writechs(disk, &tp->endh, end-1);
putle32(tp->xlba, lba-xbase);
putle32(tp->xsize, end-lba);
}
static int
wrextend(Edit *edit, int i, int64_t xbase, int64_t startlba,
int64_t *endlba)
{
int ni;
Table table;
Tentry *tp, *ep;
Dospart *p;
Disk *disk;
if(i == edit->npart){
*endlba = edit->disk->secs;
Finish:
if(startlba < *endlba){
disk = edit->disk;
diskread(disk, &table, Tablesz, mbroffset+startlba, Toffset);
tp = table.entry;
ep = tp+NTentry;
for(; tp<ep; tp++)
memset(tp, 0, sizeof *tp);
table.magic[0] = Magic0;
table.magic[1] = Magic1;
if(diskwrite(edit->disk, &table, Tablesz, mbroffset+startlba, Toffset) < 0)
recover(edit);
}
return i;
}
p = (Dospart*)edit->part[i];
if(p->primary){
*endlba = (int64_t)p->start*sec2cyl;
goto Finish;
}
disk = edit->disk;
diskread(disk, &table, Tablesz, mbroffset+startlba, Toffset);
tp = table.entry;
ep = tp+NTentry;
ni = wrextend(edit, i+1, xbase, p->end*sec2cyl, endlba);
*tp = p->Tentry;
wrtentry(disk, tp, p->type, startlba, startlba+disk->s, p->end*sec2cyl);
tp++;
if(p->end*sec2cyl != *endlba){
memset(tp, 0, sizeof *tp);
wrtentry(disk, tp, TypeEXTENDED, xbase, p->end*sec2cyl, *endlba);
tp++;
}
for(; tp<ep; tp++)
memset(tp, 0, sizeof *tp);
table.magic[0] = Magic0;
table.magic[1] = Magic1;
if(diskwrite(edit->disk, &table, Tablesz, mbroffset+startlba, Toffset) < 0)
recover(edit);
return ni;
}
static void
wrpart(Edit *edit)
{
int i, ni, t;
Table table;
Tentry *tp, *ep;
Disk *disk;
int64_t s, endlba;
Dospart *p;
disk = edit->disk;
diskread(disk, &table, Tablesz, mbroffset, Toffset);
tp = table.entry;
ep = tp+NTentry;
for(i=0; i<edit->npart && tp<ep; ) {
p = (Dospart*)edit->part[i];
if(p->start == 0)
s = disk->s;
else
s = p->start*sec2cyl;
if(p->primary) {
*tp = p->Tentry;
wrtentry(disk, tp, p->type, 0, s, p->end*sec2cyl);
tp++;
i++;
} else {
ni = wrextend(edit, i, p->start*sec2cyl, p->start*sec2cyl, &endlba);
memset(tp, 0, sizeof *tp);
if(endlba >= 1024*sec2cyl)
t = TypeEXTHUGE;
else
t = TypeEXTENDED;
wrtentry(disk, tp, t, 0, s, endlba);
tp++;
i = ni;
}
}
for(; tp<ep; tp++)
memset(tp, 0, sizeof(*tp));
if(i != edit->npart)
sysfatal("cannot happen #1");
if(diskwrite(disk, &table, Tablesz, mbroffset, Toffset) < 0)
recover(edit);
/* bring parts up to date */
freenamelist();
for(i=0; i<edit->npart; i++)
plan9print((Dospart*)edit->part[i], -1);
if(ctldiff(edit, disk->ctlfd) < 0)
fprint(2, "?warning: partitions could not be updated in devsd\n");
}