xref: /netbsd/distrib/utils/edlabel/edlabel.c (revision 6c6ab6a9)
1 /*	$NetBSD: edlabel.c,v 1.1 2010/03/10 23:16:16 abs Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Gordon W. Ross
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #define FSTYPENAMES
33 #include <sys/disklabel.h>
34 
35 #include <fcntl.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <util.h>
42 #include <stdlib.h>
43 
44 /*
45  * Machine dependent constants you want to retrieve only once...
46  */
47 int rawpartition, maxpartitions;
48 
49 /*
50  * This is a data-driven program
51  */
52 struct field {
53 	const char *f_name;
54 	int f_offset;
55 	int f_type;	/* 1:char, 2:short, 4:int, >4:string */
56 };
57 
58 /* Table describing fields in the head of a disklabel. */
59 #define	dloff(f) (int)(&((struct disklabel *)0)->f)
60 struct field label_head[] = {
61   { "        type_num", dloff(d_type), 2 },
62   { "        sub_type", dloff(d_subtype), 2 },
63   { "       type_name", dloff(d_typename), 16 },
64   { "       pack_name", dloff(d_packname),  16 },
65   { "    bytes/sector", dloff(d_secsize), 4 },
66   { "   sectors/track", dloff(d_nsectors), 4 },
67   { " tracks/cylinder", dloff(d_ntracks),  4 },
68   { "       cylinders", dloff(d_ncylinders), 4 },
69   { "sectors/cylinder", dloff(d_secpercyl), 4 },
70   /* Don't care about the others until later... */
71   { .f_name = NULL },
72 };
73 #undef dloff
74 
75 void	check_divisors(struct disklabel *);
76 u_short	dkcksum(struct disklabel *);
77 void	edit_geo(struct disklabel *);
78 void	edit_head_all(struct disklabel *, int);
79 void	edit_head_field(void *, struct field *, int);
80 void	edit_partition(struct disklabel *, int, int);
81 void	get_fstype(char *, u_int8_t *);
82 void	get_val_cts(struct disklabel *, char *, u_int32_t *);
83 void	label_modify(struct disklabel *, char *);
84 void	label_print(struct disklabel *, char *);
85 void	label_quit(struct disklabel *, char *);
86 void	label_read(struct disklabel *, char *);
87 void	label_write(struct disklabel *, char *);
88 void	menu(void);
89 void	print_val_cts(struct disklabel *, u_long val);
90 
91 char	tmpbuf[64];
92 
93 void
edit_head_field(void * v,struct field * f,int modify)94 edit_head_field(void *v, struct field *f, int modify /* also modify */)
95 {
96 	u_int8_t  *cp;
97 	u_int tmp;
98 
99 	cp = v;
100 	cp += f->f_offset;
101 
102 	printf("%s: ", f->f_name);
103 
104 	/* Print current value... */
105 	switch (f->f_type) {
106 	case 1:
107 		tmp = *cp;
108 		printf("%d", tmp);
109 		break;
110 	case 2:
111 		tmp = *((u_int16_t *)cp);
112 		printf("%d", tmp);
113 		break;
114 	case 4:
115 		tmp = *((u_int32_t *)cp);
116 		printf("%d", tmp);
117 		break;
118 
119 	default:
120 		/* must be a string. */
121 		strlcpy(tmpbuf, (char*)cp, sizeof(tmpbuf));
122 		printf("%s", tmpbuf);
123 		break;
124 	}
125 
126 	if (modify == 0) {
127 		printf("\n");
128 		return;
129 	}
130 	printf(" ? ");
131 	fflush(stdout);
132 
133 	tmpbuf[0] = '\0';
134 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
135 		return;
136 	if ((tmpbuf[0] == '\0') || (tmpbuf[0] == '\n')) {
137 		/* no new value supplied. */
138 		return;
139 	}
140 
141 	/* store new value */
142 	if (f->f_type <= 4)
143 		if (sscanf(tmpbuf, "%d", &tmp) != 1)
144 			return;
145 
146 	switch (f->f_type) {
147 	case 1:
148 		*cp = tmp;
149 		break;
150 	case 2:
151 		*((u_int16_t *)cp) = tmp;
152 		break;
153 	case 4:
154 		*((u_int32_t *)cp) = tmp;
155 		break;
156 	default:
157 		/* Get rid of the trailing newline. */
158 		tmp = strlen(tmpbuf);
159 		if (tmp < 1)
160 			break;
161 		if (tmpbuf[tmp-1] == '\n')
162 			tmpbuf[tmp-1] = '\0';
163 		strncpy((char*)cp, tmpbuf, f->f_type);
164 		break;
165 	}
166 }
167 
168 void
edit_head_all(struct disklabel * d,int modify)169 edit_head_all(struct disklabel *d, int modify)
170 {
171 	struct field *f;
172 
173 	/* Edit head stuff. */
174 	for (f = label_head; f->f_name; f++)
175 		edit_head_field(d, f, modify);
176 }
177 
178 void
edit_geo(struct disklabel * d)179 edit_geo(struct disklabel *d)
180 {
181 	int nsect, ntrack, ncyl, spc;
182 
183 	nsect = ntrack = ncyl = spc = 0;
184 
185 	printf("Sectors/track: ");
186 	fflush(stdout);
187 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
188 		return;
189 	if (sscanf(tmpbuf, "%d", &nsect) != 1)
190 		nsect = d->d_nsectors;
191 	printf("Track/cyl: ");
192 	fflush(stdout);
193 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
194 		return;
195 	if (sscanf(tmpbuf, "%d", &ntrack) != 1)
196 		ntrack = d->d_ntracks;
197 	if (!nsect || !ntrack)
198 		return;
199 	spc = nsect * ntrack;
200 	if (!(ncyl = d->d_secperunit / spc))
201 		return;
202 	d->d_nsectors   = nsect;
203 	d->d_ntracks    = ntrack;
204 	d->d_ncylinders = ncyl;
205 	d->d_secpercyl  = spc;
206 }
207 
208 void
print_val_cts(struct disklabel * d,u_long val)209 print_val_cts(struct disklabel *d, u_long val)
210 {
211 	int	sects, trks, cyls;
212 	char	marker;
213 	char	buf[80];
214 
215 	marker = (val % d->d_secpercyl) ? '*' : ' ';
216 	sects  = val % d->d_nsectors;
217 	cyls   = val / d->d_nsectors;
218 	trks   = cyls % d->d_ntracks;
219 	cyls  /= d->d_ntracks;
220 	snprintf(buf, sizeof(buf), "(%d/%02d/%02d)%c", cyls, trks, sects,
221 	    marker);
222 	printf(" %9ld %16s", val, buf);
223 }
224 
225 void
get_val_cts(struct disklabel * d,char * buf,u_int32_t * result)226 get_val_cts(struct disklabel *d, char *buf, u_int32_t *result)
227 {
228 	u_long tmp;
229 	int	cyls, trks, sects;
230 
231 	tmp = sscanf(buf, "%d/%d/%d", &cyls, &trks, &sects);
232 	if (tmp == 1)
233 		*result = cyls;	/* really nblks! */
234 	if (tmp == 3) {
235 		tmp = cyls;
236 		tmp *= d->d_ntracks;
237 		tmp += trks;
238 		tmp *= d->d_nsectors;
239 		tmp += sects;
240 		*result = tmp;
241 	}
242 }
243 
244 void
get_fstype(char * buf,u_int8_t * fstype)245 get_fstype(char *buf, u_int8_t *fstype)
246 {
247 	int	i, len;
248 
249 	/* An empty response retains previous value */
250 	if (buf[0] == '\n')
251 		return;
252 	for (i = 0, len = strlen(buf) - 1; i < FSMAXTYPES; i++) {
253 		if (!strncasecmp(buf, fstypenames[i], len)) {
254 			*fstype = i;
255 			return;
256 		}
257 	}
258 }
259 
260 void
edit_partition(struct disklabel * d,int idx,int modify)261 edit_partition(struct disklabel *d, int idx, int modify)
262 {
263 	struct partition *p;
264 	char letter;
265 	const char *comment;
266 
267 	if ((idx < 0) || (idx >= maxpartitions)) {
268 		printf("bad partition index\n");
269 		return;
270 	}
271 
272 	p = &d->d_partitions[idx];
273 	letter = 'a' + idx;
274 
275 	/* Set hint about partition type */
276 	if (idx == rawpartition)
277 		comment = "disk";
278 	else {
279 		comment = "user";
280 		switch(idx) {
281 			case 0:
282 				comment = "root";
283 			break;
284 			case 1:
285 				comment = "swap";
286 				break;
287 		}
288 	}
289 
290 	/* Print current value... */
291 	printf(" %c (%s) ", letter, comment);
292 	print_val_cts(d, p->p_offset);
293 	print_val_cts(d, p->p_size);
294 	printf(" %s\n", fstypenames[p->p_fstype]);
295 
296 	if (modify == 0)
297 		return;
298 
299 	/* starting block, or cyls/trks/sects */
300 	printf("start as <blkno> or <cyls/trks/sects> : ");
301 	fflush(stdout);
302 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
303 		return;
304 	get_val_cts(d, tmpbuf, &p->p_offset);
305 
306 	/* number of blocks, or cyls/trks/sects */
307 	printf("length as <nblks> or <cyls/trks/sects> : ");
308 	fflush(stdout);
309 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
310 		return;
311 	get_val_cts(d, tmpbuf, &p->p_size);
312 	/* partition type */
313 	printf("type: ");
314 	fflush(stdout);
315 	if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
316 		return;
317 	get_fstype(tmpbuf, &p->p_fstype);
318 }
319 
320 /*****************************************************************/
321 
322 void
check_divisors(struct disklabel * d)323 check_divisors(struct disklabel *d)
324 {
325 	if (d->d_nsectors == 0) {
326 		d->d_nsectors = 1;
327 		printf("bad sect/trk, set to 1\n");
328 	}
329 	if (d->d_ntracks == 0) {
330 		d->d_ntracks = 1;
331 		printf("bad trks/cyl, set to 1\n");
332 	}
333 	if (d->d_ncylinders == 0) {
334 		d->d_ncylinders = 1;
335 		printf("bad cyls, set to 1\n");
336 	}
337 	if (d->d_secpercyl == 0) {
338 		d->d_secpercyl = (d->d_nsectors * d->d_ntracks);
339 		printf("bad sect/cyl, set to %d\n", d->d_secpercyl);
340 	}
341 
342 }
343 
344 u_short
dkcksum(struct disklabel * d)345 dkcksum(struct disklabel *d)
346 {
347 	u_short *start, *end;
348 	u_short sum = 0;
349 
350 	start = (u_short *)d;
351 	end = (u_short *)&d->d_partitions[d->d_npartitions];
352 	while (start < end)
353 		sum ^= *start++;
354 	return (sum);
355 }
356 
357 void
label_write(struct disklabel * d,char * dn)358 label_write(struct disklabel *d, char *dn)
359 {
360 	int fd;
361 
362 	d->d_magic = DISKMAGIC;
363 	d->d_magic2 = DISKMAGIC;
364 	d->d_checksum = 0;
365 	d->d_checksum = dkcksum(d);
366 
367 	fd = open(dn, O_RDWR, 0);
368 	if (fd < 0) {
369 		perror(dn);
370 		return;
371 	}
372 	if (ioctl(fd, DIOCWDINFO, d) < 0) {
373 		perror("ioctl DIOCWDINFO");
374 	}
375 	close(fd);
376 }
377 
378 void
label_read(struct disklabel * dl,char * dn)379 label_read(struct disklabel *dl, char *dn)
380 {
381 	int fd;
382 
383 	fd = open(dn, O_RDONLY, 0);
384 	if (fd < 0) {
385 		perror(dn);
386 		exit(1);
387 	}
388 	if (ioctl(fd, DIOCGDINFO, dl) < 0) {
389 		if (errno == ESRCH)
390 			fprintf(stderr, "edlabel: No disk label on disk\n");
391 		else
392 		    	perror("ioctl DIOCGDINFO");
393 		exit(1);
394 	}
395 
396 	/* Make sure divisors are non-zero. */
397 	check_divisors(dl);
398 
399 	close(fd);
400 }
401 
402 /*****************************************************************/
403 
404 void
label_print(struct disklabel * dl,char * dn)405 label_print(struct disklabel *dl, char *dn)
406 {
407 	int i;
408 
409 	/* Print out head stuff. */
410 	edit_head_all(dl, 0);
411 
412 	/* And the partition header. */
413 	printf("partition%6sstart%9s(c/t/s)%6snblks%9s(c/t/s)  type\n\n"
414 							"", "", "", "", "");
415 	for (i = 0; i < dl->d_npartitions; i++)
416 		edit_partition(dl, i, 0);
417 }
418 
419 char modify_cmds[] = "modify subcommands:\n\
420  @   : modify disk parameters\n\
421  a-%c : modify partition\n%s\
422  q   : quit this subcommand\n";
423 
424 void
label_modify(struct disklabel * dl,char * dn)425 label_modify(struct disklabel *dl, char *dn)
426 {
427 	int c, i;
428 	int scsi_fict = 0;
429 
430 	if (!strcmp(dl->d_typename, "SCSI disk")
431 	     && !strcmp(dl->d_packname, "fictitious"))
432 		scsi_fict = 1;
433 
434 	printf(modify_cmds, 'a' + maxpartitions - 1,
435 		scsi_fict ? " s   : standardize geometry\n" : "");
436 	for (;;) {
437 		printf("edlabel/modify> ");
438 		fflush(stdout);
439 		if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
440 			break;
441 		c = tmpbuf[0];
442 		if ((c == '\0') || (c == '\n'))
443 			continue;	/* blank line */
444 		if (c == 'q')
445 			break;
446 		if (c == '@') {
447 			edit_head_all(dl, 1);
448 			check_divisors(dl);
449 			continue;
450 		}
451 		if ((c == 's') && scsi_fict) {
452 			edit_geo(dl);
453 			continue;
454 		}
455 		if ((c < 'a') || (c > 'q')) {
456 			printf("bad input.  ");
457 			printf(modify_cmds, 'a' + maxpartitions - 1,
458 			    scsi_fict ? " s   : standardize geometry\n" : "");
459 			continue;
460 		}
461 		edit_partition(dl, c - 'a', 1);
462 	}
463 	/* Set the d_npartitions field correctly */
464 	for (i = 0; i < maxpartitions; i++) {
465 		if (dl->d_partitions[i].p_size)
466 			dl->d_npartitions = i + 1;
467 	}
468 
469 }
470 
471 void
label_quit(struct disklabel * dl,char * dn)472 label_quit(struct disklabel *dl, char *dn)
473 {
474 	exit(0);
475 }
476 
477 struct cmd {
478 	void (*cmd_func)(struct disklabel *, char *);
479 	const char *cmd_name;
480 	const char *cmd_descr;
481 } cmds[] = {
482 	{ label_print,  "print",  "display the current disk label" },
483 	{ label_modify, "modify", "prompt for changes to the label" },
484 	{ label_write,  "write",  "write the new label to disk" },
485 	{ label_quit,   "quit",   "terminate program" },
486 	{ .cmd_func = 0 },
487 };
488 
489 void
menu(void)490 menu(void)
491 {
492 	struct cmd *cmd;
493 
494 	printf("edlabel menu:\n");
495 	for (cmd = cmds; cmd->cmd_func; cmd++)
496 		printf("%s\t- %s\n", cmd->cmd_name, cmd->cmd_descr);
497 }
498 
499 int
main(int argc,char ** argv)500 main(int argc, char **argv)
501 {
502 	struct disklabel dl;
503 	struct cmd *cmd;
504 	char *dev_name;
505 
506 	if (argc != 2) {
507 		fprintf(stderr, "usage: edlabel RAWDISK\n");
508 		exit(1);
509 	}
510 	dev_name = argv[1];
511 
512 	rawpartition = getrawpartition();
513 	maxpartitions = getmaxpartitions();
514 
515 	label_read(&dl, dev_name);
516 
517 	menu();
518 
519 	for (;;) {
520 		printf("edlabel> ");
521 		fflush(stdout);
522 		if (fgets(tmpbuf, sizeof(tmpbuf), stdin) == NULL)
523 			break;
524 		for (cmd = cmds; cmd->cmd_func; cmd++)
525 			if (cmd->cmd_name[0] == tmpbuf[0])
526 				goto found;
527 		printf("Invalid command.  ");
528 		menu();
529 		continue;
530 
531 	found:
532 		cmd->cmd_func(&dl, dev_name);
533 	}
534 	exit(0);
535 }
536