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