diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/disk/prep/edit.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/disk/prep/edit.c')
-rwxr-xr-x | sys/src/cmd/disk/prep/edit.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/sys/src/cmd/disk/prep/edit.c b/sys/src/cmd/disk/prep/edit.c new file mode 100755 index 000000000..8a96116fa --- /dev/null +++ b/sys/src/cmd/disk/prep/edit.c @@ -0,0 +1,551 @@ +/* + * disk partition editor + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> +#include <disk.h> +#include "edit.h" + +char* +getline(Edit *edit) +{ + static int inited; + static Biobuf bin; + char *p; + int n; + + if(!inited){ + Binit(&bin, 0, OREAD); + inited = 1; + } + p = Brdline(&bin, '\n'); + n = Blinelen(&bin); + if(p == nil || n < 1){ + if(edit->changed) + fprint(2, "?warning: changes not written\n"); + exits(0); + } + p[n - 1] = '\0'; + while(isspace(*p)) + p++; + return p; +} + +Part* +findpart(Edit *edit, char *name) +{ + int i; + + for(i=0; i<edit->npart; i++) + if(strcmp(edit->part[i]->name, name) == 0) + return edit->part[i]; + return nil; +} + +static char* +okname(Edit *edit, char *name) +{ + int i; + static char msg[100]; + + if(name[0] == '\0') + return "partition has no name"; + +// if(strlen(name) >= NAMELEN) +// return "name too long"; +// + for(i=0; i<edit->npart; i++) { + if(strcmp(name, edit->part[i]->name) == 0) { + sprint(msg, "already have partition with name \"%s\"", name); + return msg; + } + } + return nil; +} + +char* +addpart(Edit *edit, Part *p) +{ + int i; + static char msg[100]; + char *err; + + if(err = okname(edit, p->name)) + return err; + + for(i=0; i<edit->npart; i++) { + if(p->start < edit->part[i]->end && edit->part[i]->start < p->end) { + sprint(msg, "\"%s\" %lld-%lld overlaps with \"%s\" %lld-%lld", + p->name, p->start, p->end, + edit->part[i]->name, edit->part[i]->start, edit->part[i]->end); + // return msg; + } + } + + if(edit->npart >= nelem(edit->part)) + return "too many partitions"; + + edit->part[i=edit->npart++] = p; + for(; i > 0 && p->start < edit->part[i-1]->start; i--) { + edit->part[i] = edit->part[i-1]; + edit->part[i-1] = p; + } + + if(p->changed) + edit->changed = 1; + return nil; +} + +char* +delpart(Edit *edit, Part *p) +{ + int i; + + for(i=0; i<edit->npart; i++) + if(edit->part[i] == p) + break; + assert(i < edit->npart); + edit->npart--; + for(; i<edit->npart; i++) + edit->part[i] = edit->part[i+1]; + + edit->changed = 1; + return nil; +} + +static char* +editdot(Edit *edit, int argc, char **argv) +{ + char *err; + vlong ndot; + + if(argc == 1) { + print("\t. %lld\n", edit->dot); + return nil; + } + + if(argc > 2) + return "args"; + + if(err = parseexpr(argv[1], edit->dot, edit->end, edit->end, &ndot)) + return err; + + edit->dot = ndot; + return nil; +} + +static char* +editadd(Edit *edit, int argc, char **argv) +{ + char *name, *err, *q; + static char msg[100]; + vlong start, end, maxend; + int i; + + if(argc < 2) + return "args"; + + name = estrdup(argv[1]); + if((err = okname(edit, name)) || (err = edit->okname(edit, name))) + return err; + + if(argc >= 3) + q = argv[2]; + else { + fprint(2, "start %s: ", edit->unit); + q = getline(edit); + } + if(err = parseexpr(q, edit->dot, edit->end, edit->end, &start)) + return err; + + if(start < 0 || start >= edit->end) + return "start out of range"; + + for(i=0; i < edit->npart; i++) { + if(edit->part[i]->start <= start && start < edit->part[i]->end) { + sprint(msg, "start %s in partition \"%s\"", edit->unit, edit->part[i]->name); + return msg; + } + } + + maxend = edit->end; + for(i=0; i < edit->npart; i++) + if(start < edit->part[i]->start && edit->part[i]->start < maxend) + maxend = edit->part[i]->start; + + if(argc >= 4) + q = argv[3]; + else { + fprint(2, "end [%lld..%lld] ", start, maxend); + q = getline(edit); + } + if(err = parseexpr(q, edit->dot, maxend, edit->end, &end)) + return err; + + if(start == end) + return "size zero partition"; + + if(end <= start || end > maxend) + return "end out of range"; + + if(argc > 4) + return "args"; + + if(err = edit->add(edit, name, start, end)) + return err; + + edit->dot = end; + return nil; +} + +static char* +editdel(Edit *edit, int argc, char **argv) +{ + Part *p; + + if(argc != 2) + return "args"; + + if((p = findpart(edit, argv[1])) == nil) + return "no such partition"; + + return edit->del(edit, p); +} + +static char *helptext = + ". [newdot] - display or set value of dot\n" + "a name [start [end]] - add partition\n" + "d name - delete partition\n" + "h - print help message\n" + "p - print partition table\n" + "P - print commands to update sd(3) device\n" + "w - write partition table\n" + "q - quit\n"; + +static char* +edithelp(Edit *edit, int, char**) +{ + print("%s", helptext); + if(edit->help) + return edit->help(edit); + return nil; +} + +static char* +editprint(Edit *edit, int argc, char**) +{ + vlong lastend; + int i; + Part **part; + + if(argc != 1) + return "args"; + + lastend = 0; + part = edit->part; + for(i=0; i<edit->npart; i++) { + if(lastend < part[i]->start) + edit->sum(edit, nil, lastend, part[i]->start); + edit->sum(edit, part[i], part[i]->start, part[i]->end); + lastend = part[i]->end; + } + if(lastend < edit->end) + edit->sum(edit, nil, lastend, edit->end); + return nil; +} + +char* +editwrite(Edit *edit, int argc, char**) +{ + int i; + char *err; + + if(argc != 1) + return "args"; + + if(edit->disk->rdonly) + return "read only"; + + err = edit->write(edit); + if(err) + return err; + for(i=0; i<edit->npart; i++) + edit->part[i]->changed = 0; + edit->changed = 0; + return nil; +} + +static char* +editquit(Edit *edit, int argc, char**) +{ + static int warned; + + if(argc != 1) { + warned = 0; + return "args"; + } + + if(edit->changed && (!edit->warned || edit->lastcmd != 'q')) { + edit->warned = 1; + return "changes unwritten"; + } + + exits(0); + return nil; /* not reached */ +} + +char* +editctlprint(Edit *edit, int argc, char **) +{ + if(argc != 1) + return "args"; + + if(edit->printctl) + edit->printctl(edit, 1); + else + ctldiff(edit, 1); + return nil; +} + +typedef struct Cmd Cmd; +struct Cmd { + char c; + char *(*fn)(Edit*, int ,char**); +}; + +Cmd cmds[] = { + '.', editdot, + 'a', editadd, + 'd', editdel, + '?', edithelp, + 'h', edithelp, + 'P', editctlprint, + 'p', editprint, + 'w', editwrite, + 'q', editquit, +}; + +void +runcmd(Edit *edit, char *cmd) +{ + char *f[10], *err; + int i, nf; + + while(*cmd && isspace(*cmd)) + cmd++; + + nf = tokenize(cmd, f, nelem(f)); + if(nf >= 10) { + fprint(2, "?\n"); + return; + } + + if(nf < 1) + return; + if(strlen(f[0]) != 1) { + fprint(2, "?\n"); + return; + } + + err = nil; + for(i=0; i<nelem(cmds); i++) { + if(cmds[i].c == f[0][0]) { + err = cmds[i].fn(edit, nf, f); + break; + } + } + if(i == nelem(cmds)){ + if(edit->ext) + err = edit->ext(edit, nf, f); + else + err = "unknown command"; + } + if(err) + fprint(2, "?%s\n", err); + edit->lastcmd = f[0][0]; +} + +static Part* +ctlmkpart(char *name, vlong start, vlong end, int changed) +{ + Part *p; + + p = emalloc(sizeof(*p)); + p->name = estrdup(name); + p->ctlname = estrdup(name); + p->start = start; + p->end = end; + p->changed = changed; + return p; +} + +static void +rdctlpart(Edit *edit) +{ + int i, nline, nf; + char *line[128]; + char buf[4096]; + vlong a, b; + char *f[5]; + Disk *disk; + + disk = edit->disk; + edit->nctlpart = 0; + seek(disk->ctlfd, 0, 0); + if(readn(disk->ctlfd, buf, sizeof buf) <= 0) { + return; + } + + nline = getfields(buf, line, nelem(line), 1, "\n"); + for(i=0; i<nline; i++){ + if(strncmp(line[i], "part ", 5) != 0) + continue; + + nf = getfields(line[i], f, nelem(f), 1, " \t\r"); + if(nf != 4 || strcmp(f[0], "part") != 0) + break; + + a = strtoll(f[2], 0, 0); + b = strtoll(f[3], 0, 0); + + if(a >= b) + break; + + /* only gather partitions contained in the disk partition we are editing */ + if(a < disk->offset || disk->offset+disk->secs < b) + continue; + + a -= disk->offset; + b -= disk->offset; + + /* the partition we are editing does not count */ + if(strcmp(f[1], disk->part) == 0) + continue; + + if(edit->nctlpart >= nelem(edit->ctlpart)) { + fprint(2, "?too many partitions in ctl file\n"); + exits("ctlpart"); + } + edit->ctlpart[edit->nctlpart++] = ctlmkpart(f[1], a, b, 0); + } +} + +static vlong +ctlstart(Part *p) +{ + if(p->ctlstart) + return p->ctlstart; + return p->start; +} + +static vlong +ctlend(Part *p) +{ + if(p->ctlend) + return p->ctlend; + return p->end; +} + +static int +areequiv(Part *p, Part *q) +{ + if(p->ctlname[0]=='\0' || q->ctlname[0]=='\0') + return 0; + + return strcmp(p->ctlname, q->ctlname) == 0 + && ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q); +} + +static void +unchange(Edit *edit, Part *p) +{ + int i; + Part *q; + + for(i=0; i<edit->nctlpart; i++) { + q = edit->ctlpart[i]; + if(p->start <= q->start && q->end <= p->end) { + q->changed = 0; + } + } +assert(p->changed == 0); +} + +int +ctldiff(Edit *edit, int ctlfd) +{ + int i, j, waserr; + Part *p; + vlong offset; + + rdctlpart(edit); + + /* everything is bogus until we prove otherwise */ + for(i=0; i<edit->nctlpart; i++) + edit->ctlpart[i]->changed = 1; + + /* + * partitions with same info have not changed, + * and neither have partitions inside them. + */ + for(i=0; i<edit->nctlpart; i++) + for(j=0; j<edit->npart; j++) + if(areequiv(edit->ctlpart[i], edit->part[j])) { + unchange(edit, edit->ctlpart[i]); + break; + } + + waserr = 0; + /* + * delete all the changed partitions except data (we'll add them back if necessary) + */ + for(i=0; i<edit->nctlpart; i++) { + p = edit->ctlpart[i]; + if(p->changed) + if(fprint(ctlfd, "delpart %s\n", p->ctlname)<0) { + fprint(2, "delpart failed: %s: %r\n", p->ctlname); + waserr = -1; + } + } + + /* + * add all the partitions from the real list; + * this is okay since adding a parition with + * information identical to what is there is a no-op. + */ + offset = edit->disk->offset; + for(i=0; i<edit->npart; i++) { + p = edit->part[i]; + if(p->ctlname[0]) { + if(fprint(ctlfd, "part %s %lld %lld\n", p->ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) { + fprint(2, "adding part failed: %s: %r\n", p->ctlname); + waserr = -1; + } + } + } + return waserr; +} + +void* +emalloc(ulong sz) +{ + void *v; + + v = malloc(sz); + if(v == nil) + sysfatal("malloc %lud fails", sz); + memset(v, 0, sz); + return v; +} + +char* +estrdup(char *s) +{ + s = strdup(s); + if(s == nil) + sysfatal("strdup (%.10s) fails", s); + return s; +} + |