xref: /plan9/sys/src/cmd/disk/prep/edit.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
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