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