1 /*
2 * disk partition editor
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 char*
getline(Edit * edit)12 getline(Edit *edit)
13 {
14 static int inited;
15 static Biobuf bin;
16 char *p;
17 int n;
18
19 if(!inited){
20 Binit(&bin, 0, OREAD);
21 inited = 1;
22 }
23 p = Brdline(&bin, '\n');
24 n = Blinelen(&bin);
25 if(p == nil || n < 1){
26 if(edit->changed)
27 fprint(2, "?warning: changes not written\n");
28 exits(0);
29 }
30 p[n - 1] = '\0';
31 while(isspace(*p))
32 p++;
33 return p;
34 }
35
36 Part*
findpart(Edit * edit,char * name)37 findpart(Edit *edit, char *name)
38 {
39 int i;
40
41 for(i=0; i<edit->npart; i++)
42 if(strcmp(edit->part[i]->name, name) == 0)
43 return edit->part[i];
44 return nil;
45 }
46
47 static char*
okname(Edit * edit,char * name)48 okname(Edit *edit, char *name)
49 {
50 int i;
51 static char msg[100];
52
53 if(name[0] == '\0')
54 return "partition has no name";
55
56 // if(strlen(name) >= NAMELEN)
57 // return "name too long";
58 //
59 for(i=0; i<edit->npart; i++) {
60 if(strcmp(name, edit->part[i]->name) == 0) {
61 sprint(msg, "already have partition with name \"%s\"", name);
62 return msg;
63 }
64 }
65 return nil;
66 }
67
68 char*
addpart(Edit * edit,Part * p)69 addpart(Edit *edit, Part *p)
70 {
71 int i;
72 static char msg[100];
73 char *err;
74
75 if(err = okname(edit, p->name))
76 return err;
77
78 for(i=0; i<edit->npart; i++) {
79 if(p->start < edit->part[i]->end && edit->part[i]->start < p->end) {
80 sprint(msg, "\"%s\" %lld-%lld overlaps with \"%s\" %lld-%lld",
81 p->name, p->start, p->end,
82 edit->part[i]->name, edit->part[i]->start, edit->part[i]->end);
83 // return msg;
84 }
85 }
86
87 if(edit->npart >= nelem(edit->part))
88 return "too many partitions";
89
90 edit->part[i=edit->npart++] = p;
91 for(; i > 0 && p->start < edit->part[i-1]->start; i--) {
92 edit->part[i] = edit->part[i-1];
93 edit->part[i-1] = p;
94 }
95
96 if(p->changed)
97 edit->changed = 1;
98 return nil;
99 }
100
101 char*
delpart(Edit * edit,Part * p)102 delpart(Edit *edit, Part *p)
103 {
104 int i;
105
106 for(i=0; i<edit->npart; i++)
107 if(edit->part[i] == p)
108 break;
109 assert(i < edit->npart);
110 edit->npart--;
111 for(; i<edit->npart; i++)
112 edit->part[i] = edit->part[i+1];
113
114 edit->changed = 1;
115 return nil;
116 }
117
118 static char*
editdot(Edit * edit,int argc,char ** argv)119 editdot(Edit *edit, int argc, char **argv)
120 {
121 char *err;
122 vlong ndot;
123
124 if(argc == 1) {
125 print("\t. %lld\n", edit->dot);
126 return nil;
127 }
128
129 if(argc > 2)
130 return "args";
131
132 if(err = parseexpr(argv[1], edit->dot, edit->end, edit->end, &ndot))
133 return err;
134
135 edit->dot = ndot;
136 return nil;
137 }
138
139 static char*
editadd(Edit * edit,int argc,char ** argv)140 editadd(Edit *edit, int argc, char **argv)
141 {
142 char *name, *err, *q;
143 static char msg[100];
144 vlong start, end, maxend;
145 int i;
146
147 if(argc < 2)
148 return "args";
149
150 name = estrdup(argv[1]);
151 if((err = okname(edit, name)) || (err = edit->okname(edit, name)))
152 return err;
153
154 if(argc >= 3)
155 q = argv[2];
156 else {
157 fprint(2, "start %s: ", edit->unit);
158 q = getline(edit);
159 }
160 if(err = parseexpr(q, edit->dot, edit->end, edit->end, &start))
161 return err;
162
163 if(start < 0 || start >= edit->end)
164 return "start out of range";
165
166 for(i=0; i < edit->npart; i++) {
167 if(edit->part[i]->start <= start && start < edit->part[i]->end) {
168 sprint(msg, "start %s in partition \"%s\"", edit->unit, edit->part[i]->name);
169 return msg;
170 }
171 }
172
173 maxend = edit->end;
174 for(i=0; i < edit->npart; i++)
175 if(start < edit->part[i]->start && edit->part[i]->start < maxend)
176 maxend = edit->part[i]->start;
177
178 if(argc >= 4)
179 q = argv[3];
180 else {
181 fprint(2, "end [%lld..%lld] ", start, maxend);
182 q = getline(edit);
183 }
184 if(err = parseexpr(q, edit->dot, maxend, edit->end, &end))
185 return err;
186
187 if(start == end)
188 return "size zero partition";
189
190 if(end <= start || end > maxend)
191 return "end out of range";
192
193 if(argc > 4)
194 return "args";
195
196 if(err = edit->add(edit, name, start, end))
197 return err;
198
199 edit->dot = end;
200 return nil;
201 }
202
203 static char*
editdel(Edit * edit,int argc,char ** argv)204 editdel(Edit *edit, int argc, char **argv)
205 {
206 Part *p;
207
208 if(argc != 2)
209 return "args";
210
211 if((p = findpart(edit, argv[1])) == nil)
212 return "no such partition";
213
214 return edit->del(edit, p);
215 }
216
217 static char *helptext =
218 ". [newdot] - display or set value of dot\n"
219 "a name [start [end]] - add partition\n"
220 "d name - delete partition\n"
221 "h - print help message\n"
222 "p - print partition table\n"
223 "P - print commands to update sd(3) device\n"
224 "w - write partition table\n"
225 "q - quit\n";
226
227 static char*
edithelp(Edit * edit,int,char **)228 edithelp(Edit *edit, int, char**)
229 {
230 print("%s", helptext);
231 if(edit->help)
232 return edit->help(edit);
233 return nil;
234 }
235
236 static char*
editprint(Edit * edit,int argc,char **)237 editprint(Edit *edit, int argc, char**)
238 {
239 vlong lastend;
240 int i;
241 Part **part;
242
243 if(argc != 1)
244 return "args";
245
246 lastend = 0;
247 part = edit->part;
248 for(i=0; i<edit->npart; i++) {
249 if(lastend < part[i]->start)
250 edit->sum(edit, nil, lastend, part[i]->start);
251 edit->sum(edit, part[i], part[i]->start, part[i]->end);
252 lastend = part[i]->end;
253 }
254 if(lastend < edit->end)
255 edit->sum(edit, nil, lastend, edit->end);
256 return nil;
257 }
258
259 char*
editwrite(Edit * edit,int argc,char **)260 editwrite(Edit *edit, int argc, char**)
261 {
262 int i;
263 char *err;
264
265 if(argc != 1)
266 return "args";
267
268 if(edit->disk->rdonly)
269 return "read only";
270
271 err = edit->write(edit);
272 if(err)
273 return err;
274 for(i=0; i<edit->npart; i++)
275 edit->part[i]->changed = 0;
276 edit->changed = 0;
277 return nil;
278 }
279
280 static char*
editquit(Edit * edit,int argc,char **)281 editquit(Edit *edit, int argc, char**)
282 {
283 static int warned;
284
285 if(argc != 1) {
286 warned = 0;
287 return "args";
288 }
289
290 if(edit->changed && (!edit->warned || edit->lastcmd != 'q')) {
291 edit->warned = 1;
292 return "changes unwritten";
293 }
294
295 exits(0);
296 return nil; /* not reached */
297 }
298
299 char*
editctlprint(Edit * edit,int argc,char **)300 editctlprint(Edit *edit, int argc, char **)
301 {
302 if(argc != 1)
303 return "args";
304
305 if(edit->printctl)
306 edit->printctl(edit, 1);
307 else
308 ctldiff(edit, 1);
309 return nil;
310 }
311
312 typedef struct Cmd Cmd;
313 struct Cmd {
314 char c;
315 char *(*fn)(Edit*, int ,char**);
316 };
317
318 Cmd cmds[] = {
319 '.', editdot,
320 'a', editadd,
321 'd', editdel,
322 '?', edithelp,
323 'h', edithelp,
324 'P', editctlprint,
325 'p', editprint,
326 'w', editwrite,
327 'q', editquit,
328 };
329
330 void
runcmd(Edit * edit,char * cmd)331 runcmd(Edit *edit, char *cmd)
332 {
333 char *f[10], *err;
334 int i, nf;
335
336 while(*cmd && isspace(*cmd))
337 cmd++;
338
339 nf = tokenize(cmd, f, nelem(f));
340 if(nf >= 10) {
341 fprint(2, "?\n");
342 return;
343 }
344
345 if(nf < 1)
346 return;
347 if(strlen(f[0]) != 1) {
348 fprint(2, "?\n");
349 return;
350 }
351
352 err = nil;
353 for(i=0; i<nelem(cmds); i++) {
354 if(cmds[i].c == f[0][0]) {
355 err = cmds[i].fn(edit, nf, f);
356 break;
357 }
358 }
359 if(i == nelem(cmds)){
360 if(edit->ext)
361 err = edit->ext(edit, nf, f);
362 else
363 err = "unknown command";
364 }
365 if(err)
366 fprint(2, "?%s\n", err);
367 edit->lastcmd = f[0][0];
368 }
369
370 static Part*
ctlmkpart(char * name,vlong start,vlong end,int changed)371 ctlmkpart(char *name, vlong start, vlong end, int changed)
372 {
373 Part *p;
374
375 p = emalloc(sizeof(*p));
376 p->name = estrdup(name);
377 p->ctlname = estrdup(name);
378 p->start = start;
379 p->end = end;
380 p->changed = changed;
381 return p;
382 }
383
384 static void
rdctlpart(Edit * edit)385 rdctlpart(Edit *edit)
386 {
387 int i, nline, nf;
388 char *line[128];
389 char buf[4096];
390 vlong a, b;
391 char *f[5];
392 Disk *disk;
393
394 disk = edit->disk;
395 edit->nctlpart = 0;
396 seek(disk->ctlfd, 0, 0);
397 if(readn(disk->ctlfd, buf, sizeof buf) <= 0) {
398 return;
399 }
400
401 nline = getfields(buf, line, nelem(line), 1, "\n");
402 for(i=0; i<nline; i++){
403 if(strncmp(line[i], "part ", 5) != 0)
404 continue;
405
406 nf = getfields(line[i], f, nelem(f), 1, " \t\r");
407 if(nf != 4 || strcmp(f[0], "part") != 0)
408 break;
409
410 a = strtoll(f[2], 0, 0);
411 b = strtoll(f[3], 0, 0);
412
413 if(a >= b)
414 break;
415
416 /* only gather partitions contained in the disk partition we are editing */
417 if(a < disk->offset || disk->offset+disk->secs < b)
418 continue;
419
420 a -= disk->offset;
421 b -= disk->offset;
422
423 /* the partition we are editing does not count */
424 if(strcmp(f[1], disk->part) == 0)
425 continue;
426
427 if(edit->nctlpart >= nelem(edit->ctlpart)) {
428 fprint(2, "?too many partitions in ctl file\n");
429 exits("ctlpart");
430 }
431 edit->ctlpart[edit->nctlpart++] = ctlmkpart(f[1], a, b, 0);
432 }
433 }
434
435 static vlong
ctlstart(Part * p)436 ctlstart(Part *p)
437 {
438 if(p->ctlstart)
439 return p->ctlstart;
440 return p->start;
441 }
442
443 static vlong
ctlend(Part * p)444 ctlend(Part *p)
445 {
446 if(p->ctlend)
447 return p->ctlend;
448 return p->end;
449 }
450
451 static int
areequiv(Part * p,Part * q)452 areequiv(Part *p, Part *q)
453 {
454 if(p->ctlname[0]=='\0' || q->ctlname[0]=='\0')
455 return 0;
456
457 return strcmp(p->ctlname, q->ctlname) == 0
458 && ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q);
459 }
460
461 static void
unchange(Edit * edit,Part * p)462 unchange(Edit *edit, Part *p)
463 {
464 int i;
465 Part *q;
466
467 for(i=0; i<edit->nctlpart; i++) {
468 q = edit->ctlpart[i];
469 if(p->start <= q->start && q->end <= p->end) {
470 q->changed = 0;
471 }
472 }
473 assert(p->changed == 0);
474 }
475
476 int
ctldiff(Edit * edit,int ctlfd)477 ctldiff(Edit *edit, int ctlfd)
478 {
479 int i, j, waserr;
480 Part *p;
481 vlong offset;
482
483 rdctlpart(edit);
484
485 /* everything is bogus until we prove otherwise */
486 for(i=0; i<edit->nctlpart; i++)
487 edit->ctlpart[i]->changed = 1;
488
489 /*
490 * partitions with same info have not changed,
491 * and neither have partitions inside them.
492 */
493 for(i=0; i<edit->nctlpart; i++)
494 for(j=0; j<edit->npart; j++)
495 if(areequiv(edit->ctlpart[i], edit->part[j])) {
496 unchange(edit, edit->ctlpart[i]);
497 break;
498 }
499
500 waserr = 0;
501 /*
502 * delete all the changed partitions except data (we'll add them back if necessary)
503 */
504 for(i=0; i<edit->nctlpart; i++) {
505 p = edit->ctlpart[i];
506 if(p->changed)
507 if(fprint(ctlfd, "delpart %s\n", p->ctlname)<0) {
508 fprint(2, "delpart failed: %s: %r\n", p->ctlname);
509 waserr = -1;
510 }
511 }
512
513 /*
514 * add all the partitions from the real list;
515 * this is okay since adding a parition with
516 * information identical to what is there is a no-op.
517 */
518 offset = edit->disk->offset;
519 for(i=0; i<edit->npart; i++) {
520 p = edit->part[i];
521 if(p->ctlname[0]) {
522 if(fprint(ctlfd, "part %s %lld %lld\n", p->ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) {
523 fprint(2, "adding part failed: %s: %r\n", p->ctlname);
524 waserr = -1;
525 }
526 }
527 }
528 return waserr;
529 }
530
531 void*
emalloc(ulong sz)532 emalloc(ulong sz)
533 {
534 void *v;
535
536 v = malloc(sz);
537 if(v == nil)
538 sysfatal("malloc %lud fails", sz);
539 memset(v, 0, sz);
540 return v;
541 }
542
543 char*
estrdup(char * s)544 estrdup(char *s)
545 {
546 s = strdup(s);
547 if(s == nil)
548 sysfatal("strdup (%.10s) fails", s);
549 return s;
550 }
551
552