ref: 2657773699da675d436705002230f2d25142f570
dir: /sys/src/cmd/disk/prep/edit.c/
/* * 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, edit->unitsz, &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, edit->unitsz, &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, edit->unitsz, &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; }