xref: /plan9-contrib/sys/src/cmd/disk/prep/prep.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 /*
2  * prep - prepare plan9 disk partition
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 #include <disk.h>
8 #include "edit.h"
9 
10 enum {
11 	Maxpath = 128,
12 };
13 
14 static int	blank;
15 static int	file;
16 static int	doautox;
17 static int	printflag;
18 static Part	**opart;
19 static int	nopart;
20 static char	*osecbuf;
21 static char	*secbuf;
22 static int	rdonly;
23 static int	dowrite;
24 static int	docache;
25 static int	donvram;
26 
27 static void	autoxpart(Edit*);
28 static Part	*mkpart(char*, vlong, vlong, int);
29 static void	rdpart(Edit*);
30 static void	wrpart(Edit*);
31 static void	checkfat(Disk*);
32 
33 static void 	cmdsum(Edit*, Part*, vlong, vlong);
34 static char 	*cmdadd(Edit*, char*, vlong, vlong);
35 static char 	*cmddel(Edit*, Part*);
36 static char 	*cmdokname(Edit*, char*);
37 static char 	*cmdwrite(Edit*);
38 static char	*cmdctlprint(Edit*, int, char**);
39 
40 Edit edit = {
41 	.add=	cmdadd,
42 	.del=	cmddel,
43 	.okname=cmdokname,
44 	.sum=	cmdsum,
45 	.write=	cmdwrite,
46 
47 	.unit=	"sector",
48 };
49 
50 typedef struct Auto Auto;
51 struct Auto
52 {
53 	char *name;
54 	uvlong	min;
55 	uvlong	max;
56 	uint weight;
57 	uchar	alloc;
58 	uvlong	size;
59 };
60 
61 #define GB (1024*1024*1024)
62 #define MB (1024*1024)
63 #define KB (1024)
64 
65 /*
66  * Order matters -- this is the layout order on disk.
67  */
68 Auto autox[] =
69 {
70 	{	"9fat",		10*MB,	100*MB,	10,	},
71 	{	"nvram",	512,	512,	1,	},
72 	{	"fscfg",	512,	512,	1,	},
73 	{	"fs",		200*MB,	0,	10,	},
74 	{	"fossil",	200*MB,	0,	4,	},
75 	{	"arenas",	500*MB,	0,	20,	},
76 	{	"isect",	25*MB,	0,	1,	},
77 	{	"other",	200*MB,	0,	4,	},
78 	{	"swap",		100*MB,	512*MB,	1,	},
79 	{	"cache",	50*MB,	1*GB,	2,	},
80 };
81 
82 void
83 usage(void)
84 {
85 	fprint(2, "usage: disk/prep [bcfprw] [-a partname]... [-s sectorsize] /dev/sdC0/plan9\n");
86 	exits("usage");
87 }
88 
89 void
90 main(int argc, char **argv)
91 {
92 	int i;
93 	char *p;
94 	Disk *disk;
95 	vlong secsize;
96 
97 	secsize = 0;
98 	ARGBEGIN{
99 	case 'a':
100 		p = EARGF(usage());
101 		for(i=0; i<nelem(autox); i++){
102 			if(strcmp(p, autox[i].name) == 0){
103 				if(autox[i].alloc){
104 					fprint(2, "you said -a %s more than once.\n", p);
105 					usage();
106 				}
107 				autox[i].alloc = 1;
108 				break;
109 			}
110 		}
111 		if(i == nelem(autox)){
112 			fprint(2, "don't know how to create autoxmatic partition %s\n", p);
113 			usage();
114 		}
115 		doautox = 1;
116 		break;
117 	case 'b':
118 		blank++;
119 		break;
120 	case 'c':
121 		docache++;
122 		break;
123 	case 'f':
124 		file++;
125 		break;
126 	case 'n':
127 		donvram++;
128 		break;
129 	case 'p':
130 		printflag++;
131 		rdonly++;
132 		break;
133 	case 'r':
134 		rdonly++;
135 		break;
136 	case 's':
137 		secsize = atoi(ARGF());
138 		break;
139 	case 'w':
140 		dowrite++;
141 		break;
142 	default:
143 		usage();
144 	}ARGEND;
145 
146 	if(argc != 1)
147 		usage();
148 
149 	disk = opendisk(argv[0], rdonly, file);
150 	if(disk == nil) {
151 		fprint(2, "cannot open disk: %r\n");
152 		exits("opendisk");
153 	}
154 
155 	if(secsize != 0) {
156 		disk->secsize = secsize;
157 		disk->secs = disk->size / secsize;
158 	}
159 	edit.end = disk->secs;
160 
161 	checkfat(disk);
162 
163 	secbuf = emalloc(disk->secsize+1);
164 	osecbuf = emalloc(disk->secsize+1);
165 	edit.disk = disk;
166 
167 	if(blank == 0)
168 		rdpart(&edit);
169 
170 	opart = emalloc(edit.npart*sizeof(opart[0]));
171 
172 	/* save old partition table */
173 	for(i=0; i<edit.npart; i++)
174 		opart[i] = edit.part[i];
175 	nopart = edit.npart;
176 
177 	if(printflag) {
178 		runcmd(&edit, "P");
179 		exits(0);
180 	}
181 
182 	if(doautox)
183 		autoxpart(&edit);
184 
185 	if(dowrite) {
186 		runcmd(&edit, "w");
187 		exits(0);
188 	}
189 
190 	runcmd(&edit, "p");
191 	for(;;) {
192 		fprint(2, ">>> ");
193 		runcmd(&edit, getline(&edit));
194 	}
195 }
196 
197 static void
198 cmdsum(Edit *edit, Part *p, vlong a, vlong b)
199 {
200 	vlong sz, div;
201 	char *suf, *name;
202 	char c;
203 
204 	c = p && p->changed ? '\'' : ' ';
205 	name = p ? p->name : "empty";
206 
207 	sz = (b-a)*edit->disk->secsize;
208 	if(sz >= 1*GB){
209 		suf = "GB";
210 		div = GB;
211 	}else if(sz >= 1*MB){
212 		suf = "MB";
213 		div = MB;
214 	}else if(sz >= 1*KB){
215 		suf = "KB";
216 		div = KB;
217 	}else{
218 		suf = "B ";
219 		div = 1;
220 	}
221 
222 	if(div == 1)
223 		print("%c %-12s %*lld %-*lld (%lld sectors, %lld %s)\n", c, name,
224 			edit->disk->width, a, edit->disk->width, b, b-a, sz, suf);
225 	else
226 		print("%c %-12s %*lld %-*lld (%lld sectors, %lld.%.2d %s)\n", c, name,
227 			edit->disk->width, a, edit->disk->width, b, b-a,
228 			sz/div, (int)(((sz%div)*100)/div), suf);
229 }
230 
231 static char*
232 cmdadd(Edit *edit, char *name, vlong start, vlong end)
233 {
234 	if(start < 2 && strcmp(name, "9fat") != 0)
235 		return "overlaps with the pbs and/or the partition table";
236 
237 	return addpart(edit, mkpart(name, start, end, 1));
238 }
239 
240 static char*
241 cmddel(Edit *edit, Part *p)
242 {
243 	return delpart(edit, p);
244 }
245 
246 static char*
247 cmdwrite(Edit *edit)
248 {
249 	wrpart(edit);
250 	return nil;
251 }
252 
253 static char isfrog[256]={
254 	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
255 	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
256 	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
257 	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
258 	[' ']	1,
259 	['/']	1,
260 	[0x7f]	1,
261 };
262 
263 static char*
264 cmdokname(Edit*, char *elem)
265 {
266 	for(; *elem; elem++)
267 		if(isfrog[*(uchar*)elem])
268 			return "bad character in name";
269 	return nil;
270 }
271 
272 static Part*
273 mkpart(char *name, vlong start, vlong end, int changed)
274 {
275 	Part *p;
276 
277 	p = emalloc(sizeof(*p));
278 	p->name = estrdup(name);
279 	p->ctlname = estrdup(name);
280 	p->start = start;
281 	p->end = end;
282 	p->changed = changed;
283 	return p;
284 }
285 
286 /* plan9 partition is first sector of the disk */
287 static void
288 rdpart(Edit *edit)
289 {
290 	int i, nline, nf, waserr;
291 	char *line[128];
292 	vlong a, b;
293 	char *f[5];
294 	char *err;
295 	Disk *disk;
296 
297 	disk = edit->disk;
298 	seek(disk->fd, disk->secsize, 0);
299 	if(readn(disk->fd, osecbuf, disk->secsize) != disk->secsize)
300 		return;
301 	osecbuf[disk->secsize] = '\0';
302 	memmove(secbuf, osecbuf, disk->secsize+1);
303 
304 	if(strncmp(secbuf, "part", 4) != 0){
305 		fprint(2, "no plan9 partition table found\n");
306 		return;
307 	}
308 
309 	waserr = 0;
310 	nline = getfields(secbuf, line, nelem(line), 1, "\n");
311 	for(i=0; i<nline; i++){
312 		if(strncmp(line[i], "part", 4) != 0) {
313 		Error:
314 			if(waserr == 0)
315 				fprint(2, "syntax error reading partition\n");
316 			waserr = 1;
317 			continue;
318 		}
319 
320 		nf = getfields(line[i], f, nelem(f), 1, " \t\r");
321 		if(nf != 4 || strcmp(f[0], "part") != 0)
322 			goto Error;
323 
324 		a = strtoll(f[2], 0, 0);
325 		b = strtoll(f[3], 0, 0);
326 		if(a >= b)
327 			goto Error;
328 
329 		if(err = addpart(edit, mkpart(f[1], a, b, 0))) {
330 			fprint(2, "?%s: not continuing\n", err);
331 			exits("partition");
332 		}
333 	}
334 }
335 
336 static vlong
337 min(vlong a, vlong b)
338 {
339 	if(a < b)
340 		return a;
341 	return b;
342 }
343 
344 static void
345 autoxpart(Edit *edit)
346 {
347 	int i, totw, futz;
348 	vlong secs, secsize, s;
349 	char *err;
350 
351 	if(edit->npart > 0) {
352 		if(doautox)
353 			fprint(2, "partitions already exist; not repartitioning\n");
354 		return;
355 	}
356 
357 	secs = edit->disk->secs;
358 	secsize = edit->disk->secsize;
359 	for(;;){
360 		/* compute total weights */
361 		totw = 0;
362 		for(i=0; i<nelem(autox); i++){
363 			if(autox[i].alloc==0 || autox[i].size)
364 				continue;
365 			totw += autox[i].weight;
366 		}
367 		if(totw == 0)
368 			break;
369 
370 		if(secs <= 0){
371 			fprint(2, "ran out of disk space during autoxpartition.\n");
372 			return;
373 		}
374 
375 		/* assign any minimums for small disks */
376 		futz = 0;
377 		for(i=0; i<nelem(autox); i++){
378 			if(autox[i].alloc==0 || autox[i].size)
379 				continue;
380 			s = (secs*autox[i].weight)/totw;
381 			if(s < autox[i].min/secsize){
382 				autox[i].size = autox[i].min/secsize;
383 				secs -= autox[i].size;
384 				futz = 1;
385 				break;
386 			}
387 		}
388 		if(futz)
389 			continue;
390 
391 		/* assign any maximums for big disks */
392 		futz = 0;
393 		for(i=0; i<nelem(autox); i++){
394 			if(autox[i].alloc==0 || autox[i].size)
395 				continue;
396 			s = (secs*autox[i].weight)/totw;
397 			if(autox[i].max && s > autox[i].max/secsize){
398 				autox[i].size = autox[i].max/secsize;
399 				secs -= autox[i].size;
400 				futz = 1;
401 				break;
402 			}
403 		}
404 		if(futz)
405 			continue;
406 
407 		/* finally, assign partition sizes according to weights */
408 		for(i=0; i<nelem(autox); i++){
409 			if(autox[i].alloc==0 || autox[i].size)
410 				continue;
411 			s = (secs*autox[i].weight)/totw;
412 			autox[i].size = s;
413 
414 			/* use entire disk even in face of rounding errors */
415 			secs -= autox[i].size;
416 			totw -= autox[i].weight;
417 		}
418 	}
419 
420 	for(i=0; i<nelem(autox); i++)
421 		if(autox[i].alloc)
422 			print("%s %llud\n", autox[i].name, autox[i].size);
423 
424 	s = 0;
425 	for(i=0; i<nelem(autox); i++){
426 		if(autox[i].alloc == 0)
427 			continue;
428 		if(err = addpart(edit, mkpart(autox[i].name, s, s+autox[i].size, 1)))
429 			fprint(2, "addpart %s: %s\n", autox[i].name, err);
430 		s += autox[i].size;
431 	}
432 }
433 
434 static void
435 restore(Edit *edit, int ctlfd)
436 {
437 	int i;
438 	vlong offset;
439 
440 	offset = edit->disk->offset;
441 	fprint(2, "attempting to restore partitions to previous state\n");
442 	if(seek(edit->disk->wfd, edit->disk->secsize, 0) != 0){
443 		fprint(2, "cannot restore: error seeking on disk\n");
444 		exits("inconsistent");
445 	}
446 
447 	if(write(edit->disk->wfd, osecbuf, edit->disk->secsize) != edit->disk->secsize){
448 		fprint(2, "cannot restore: couldn't write old partition table to disk\n");
449 		exits("inconsistent");
450 	}
451 
452 	if(ctlfd >= 0){
453 		for(i=0; i<edit->npart; i++)
454 			fprint(ctlfd, "delpart %s", edit->part[i]->name);
455 		for(i=0; i<nopart; i++){
456 			if(fprint(ctlfd, "part %s %lld %lld", opart[i]->name, opart[i]->start+offset, opart[i]->end+offset) < 0){
457 				fprint(2, "restored disk partition table but not kernel; reboot\n");
458 				exits("inconsistent");
459 			}
460 		}
461 	}
462 	exits("restored");
463 }
464 
465 static void
466 wrpart(Edit *edit)
467 {
468 	int i, n;
469 	Disk *disk;
470 
471 	disk = edit->disk;
472 
473 	memset(secbuf, 0, disk->secsize);
474 	n = 0;
475 	for(i=0; i<edit->npart; i++)
476 		n += snprint(secbuf+n, disk->secsize-n, "part %s %lld %lld\n",
477 			edit->part[i]->name, edit->part[i]->start, edit->part[i]->end);
478 
479 	if(seek(disk->wfd, disk->secsize, 0) != disk->secsize){
480 		fprint(2, "error seeking %d %lld on disk: %r\n", disk->wfd, disk->secsize);
481 		exits("seek");
482 	}
483 
484 	if(write(disk->wfd, secbuf, disk->secsize) != disk->secsize){
485 		fprint(2, "error writing partition table to disk\n");
486 		restore(edit, -1);
487 	}
488 
489 	if(ctldiff(edit, disk->ctlfd) < 0)
490 		fprint(2, "?warning: partitions could not be updated in devsd\n");
491 }
492 
493 /*
494  * Look for a boot sector in sector 1, as would be
495  * the case if editing /dev/sdC0/data when that
496  * was really a bootable disk.
497  */
498 static void
499 checkfat(Disk *disk)
500 {
501 	uchar buf[32];
502 
503 	if(seek(disk->fd, disk->secsize, 0) < 0
504 	|| read(disk->fd, buf, sizeof(buf)) < sizeof(buf))
505 		return;
506 
507 	if(buf[0] != 0xEB || buf[1] != 0x3C || buf[2] != 0x90)
508 		return;
509 
510 	fprint(2,
511 		"there's a fat partition where the\n"
512 		"plan9 partition table would go.\n"
513 		"if you really want to overwrite it, zero\n"
514 		"the second sector of the disk and try again\n");
515 
516 	exits("fat partition");
517 }
518