summaryrefslogtreecommitdiff
path: root/sys/src/cmd/disk/prep/edit.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/disk/prep/edit.c551
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;
+}
+