xref: /original-bsd/usr.sbin/bad144/bad144.c (revision 77936e01)
1 /*
2  * Copyright (c) 1980,1986,1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980,1986,1988 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif not lint
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)bad144.c	5.17 (Berkeley) 01/30/90";
26 #endif not lint
27 
28 /*
29  * bad144
30  *
31  * This program prints and/or initializes a bad block record for a pack,
32  * in the format used by the DEC standard 144.
33  * It can also add bad sector(s) to the record, moving the sector
34  * replacements as necessary.
35  *
36  * It is preferable to write the bad information with a standard formatter,
37  * but this program will do.
38  *
39  * RP06 sectors are marked as bad by inverting the format bit in the
40  * header; on other drives the valid-sector bit is cleared.
41  */
42 #include <sys/param.h>
43 #include <sys/dkbad.h>
44 #include <sys/ioctl.h>
45 #include <ufs/fs.h>
46 #include <sys/file.h>
47 #include <sys/disklabel.h>
48 
49 #include <stdio.h>
50 #include <paths.h>
51 
52 #define RETRIES	10		/* number of retries on reading old sectors */
53 #define	RAWPART	"c"		/* disk partition containing badsector tables */
54 
55 int	fflag, add, copy, verbose, nflag;
56 int	compare();
57 int	dups;
58 int	badfile = -1;		/* copy of badsector table to use, -1 if any */
59 #define MAXSECSIZE	1024
60 struct	dkbad curbad, oldbad;
61 #define	DKBAD_MAGIC	0
62 
63 char	label[BBSIZE];
64 daddr_t	size, getold(), badsn();
65 struct	disklabel *dp;
66 char	name[BUFSIZ];
67 char	*malloc();
68 off_t	lseek();
69 
70 main(argc, argv)
71 	int argc;
72 	char *argv[];
73 {
74 	register struct bt_bad *bt;
75 	daddr_t	sn, bn[126];
76 	int i, f, nbad, new, bad, errs;
77 
78 	argc--, argv++;
79 	while (argc > 0 && **argv == '-') {
80 		(*argv)++;
81 		while (**argv) {
82 			switch (**argv) {
83 #if vax
84 			    case 'f':
85 				fflag++;
86 				break;
87 #endif
88 			    case 'a':
89 				add++;
90 				break;
91 			    case 'c':
92 				copy++;
93 				break;
94 			    case 'v':
95 				verbose++;
96 				break;
97 			    case 'n':
98 				nflag++;
99 				verbose++;
100 				break;
101 			    default:
102 				if (**argv >= '0' && **argv <= '4') {
103 					badfile = **argv - '0';
104 					break;
105 				}
106 				goto usage;
107 			}
108 			(*argv)++;
109 		}
110 		argc--, argv++;
111 	}
112 	if (argc < 1) {
113 usage:
114 		fprintf(stderr,
115 		  "usage: bad144 [ -f ] disk [ snum [ bn ... ] ]\n");
116 		fprintf(stderr,
117 	      "to read or overwrite bad-sector table, e.g.: bad144 hp0\n");
118 		fprintf(stderr,
119 		  "or bad144 -a [ -f ] [ -c ] disk  bn ...\n");
120 		fprintf(stderr, "where options are:\n");
121 		fprintf(stderr, "\t-a  add new bad sectors to the table\n");
122 		fprintf(stderr, "\t-f  reformat listed sectors as bad\n");
123 		fprintf(stderr, "\t-c  copy original sector to replacement\n");
124 		exit(1);
125 	}
126 	if (argv[0][0] != '/')
127 		(void)sprintf(name, "%s/r%s%s", _PATH_DEV, argv[0], RAWPART);
128 	else
129 		strcpy(name, argv[0]);
130 	f = open(name, argc == 1? O_RDONLY : O_RDWR);
131 	if (f < 0)
132 		Perror(name);
133 	if (read(f, label, sizeof(label)) < 0)
134 		Perror("read");
135 	for (dp = (struct disklabel *)(label + LABELOFFSET);
136 	    dp < (struct disklabel *)
137 		(label + sizeof(label) - sizeof(struct disklabel));
138 	    dp = (struct disklabel *)((char *)dp + 64))
139 		if (dp->d_magic == DISKMAGIC && dp->d_magic2 == DISKMAGIC)
140 			break;
141 	if (dp->d_magic != DISKMAGIC || dp->d_magic2 != DISKMAGIC) {
142 		fprintf(stderr, "Bad pack magic number (pack is unlabeled)\n");
143 		exit(1);
144 	}
145 	if (dp->d_secsize > MAXSECSIZE || dp->d_secsize <= 0) {
146 		fprintf(stderr, "Disk sector size too large/small (%d)\n",
147 			dp->d_secsize);
148 		exit(7);
149 	}
150 	size = dp->d_nsectors * dp->d_ntracks * dp->d_ncylinders;
151 	argc--;
152 	argv++;
153 	if (argc == 0) {
154 		sn = getold(f, &oldbad);
155 		printf("bad block information at sector %d in %s:\n",
156 		    sn, name);
157 		printf("cartridge serial number: %d(10)\n", oldbad.bt_csn);
158 		switch (oldbad.bt_flag) {
159 
160 		case (u_short)-1:
161 			printf("alignment cartridge\n");
162 			break;
163 
164 		case DKBAD_MAGIC:
165 			break;
166 
167 		default:
168 			printf("bt_flag=%x(16)?\n", oldbad.bt_flag);
169 			break;
170 		}
171 		bt = oldbad.bt_bad;
172 		for (i = 0; i < 126; i++) {
173 			bad = (bt->bt_cyl<<16) + bt->bt_trksec;
174 			if (bad < 0)
175 				break;
176 			printf("sn=%d, cn=%d, tn=%d, sn=%d\n", badsn(bt),
177 			    bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
178 			bt++;
179 		}
180 		(void) checkold(&oldbad);
181 		exit(0);
182 	}
183 	if (add) {
184 		/*
185 		 * Read in the old badsector table.
186 		 * Verify that it makes sense, and the bad sectors
187 		 * are in order.  Copy the old table to the new one.
188 		 */
189 		(void) getold(f, &oldbad);
190 		i = checkold(&oldbad);
191 		if (verbose)
192 			printf("Had %d bad sectors, adding %d\n", i, argc);
193 		if (i + argc > 126) {
194 			printf("bad144: not enough room for %d more sectors\n",
195 				argc);
196 			printf("limited to 126 by information format\n");
197 			exit(1);
198 		}
199 		curbad = oldbad;
200 	} else {
201 		curbad.bt_csn = atoi(*argv++);
202 		argc--;
203 		curbad.bt_mbz = 0;
204 		curbad.bt_flag = DKBAD_MAGIC;
205 		if (argc > 126) {
206 			printf("bad144: too many bad sectors specified\n");
207 			printf("limited to 126 by information format\n");
208 			exit(1);
209 		}
210 		i = 0;
211 	}
212 	errs = 0;
213 	new = argc;
214 	while (argc > 0) {
215 		daddr_t sn = atoi(*argv++);
216 		argc--;
217 		if (sn < 0 || sn >= size) {
218 			printf("%d: out of range [0,%d) for disk %s\n",
219 			    sn, size, dp->d_typename);
220 			errs++;
221 			continue;
222 		}
223 		bn[i] = sn;
224 		curbad.bt_bad[i].bt_cyl = sn / (dp->d_nsectors*dp->d_ntracks);
225 		sn %= (dp->d_nsectors*dp->d_ntracks);
226 		curbad.bt_bad[i].bt_trksec =
227 		    ((sn/dp->d_nsectors) << 8) + (sn%dp->d_nsectors);
228 		i++;
229 	}
230 	if (errs)
231 		exit(1);
232 	nbad = i;
233 	while (i < 126) {
234 		curbad.bt_bad[i].bt_trksec = -1;
235 		curbad.bt_bad[i].bt_cyl = -1;
236 		i++;
237 	}
238 	if (add) {
239 		/*
240 		 * Sort the new bad sectors into the list.
241 		 * Then shuffle the replacement sectors so that
242 		 * the previous bad sectors get the same replacement data.
243 		 */
244 		qsort((char *)curbad.bt_bad, nbad, sizeof (struct bt_bad),
245 		    compare);
246 		if (dups) {
247 			fprintf(stderr,
248 "bad144: bad sectors have been duplicated; can't add existing sectors\n");
249 			exit(3);
250 		}
251 		shift(f, nbad, nbad-new);
252 	}
253 	if (badfile == -1)
254 		i = 0;
255 	else
256 		i = badfile * 2;
257 	for (; i < 10 && i < dp->d_nsectors; i += 2) {
258 		if (lseek(f, dp->d_secsize * (size - dp->d_nsectors + i),
259 		    L_SET) < 0)
260 			Perror("lseek");
261 		if (verbose)
262 			printf("write badsect file at %d\n",
263 				size - dp->d_nsectors + i);
264 		if (nflag == 0 && write(f, (caddr_t)&curbad, sizeof(curbad)) !=
265 		    sizeof(curbad)) {
266 			char msg[80];
267 			(void)sprintf(msg, "bad144: write bad sector file %d",
268 			    i/2);
269 			perror(msg);
270 		}
271 		if (badfile != -1)
272 			break;
273 	}
274 #ifdef vax
275 	if (nflag == 0 && fflag)
276 		for (i = nbad - new; i < nbad; i++)
277 			format(f, bn[i]);
278 #endif
279 #ifdef DIOCSBAD
280 	if (nflag == 0 && ioctl(f, DIOCSBAD, (caddr_t)&curbad) < 0)
281 		fprintf(stderr,
282 	"Can't sync bad-sector file; reboot for changes to take effect\n");
283 #endif
284 	exit(0);
285 }
286 
287 daddr_t
288 getold(f, bad)
289 struct dkbad *bad;
290 {
291 	register int i;
292 	daddr_t sn;
293 	char msg[80];
294 
295 	if (badfile == -1)
296 		i = 0;
297 	else
298 		i = badfile * 2;
299 	for (; i < 10 && i < dp->d_nsectors; i += 2) {
300 		sn = size - dp->d_nsectors + i;
301 		if (lseek(f, sn * dp->d_secsize, L_SET) < 0)
302 			Perror("lseek");
303 		if (read(f, (char *) bad, dp->d_secsize) == dp->d_secsize) {
304 			if (i > 0)
305 				printf("Using bad-sector file %d\n", i/2);
306 			return(sn);
307 		}
308 		(void)sprintf(msg, "bad144: read bad sector file at sn %d", sn);
309 		perror(msg);
310 		if (badfile != -1)
311 			break;
312 	}
313 	fprintf(stderr, "bad144: %s: can't read bad block info\n", name);
314 	exit(1);
315 	/*NOTREACHED*/
316 }
317 
318 checkold()
319 {
320 	register int i;
321 	register struct bt_bad *bt;
322 	daddr_t sn, lsn;
323 	int errors = 0, warned = 0;
324 
325 	if (oldbad.bt_flag != DKBAD_MAGIC) {
326 		fprintf(stderr, "bad144: %s: bad flag in bad-sector table\n",
327 			name);
328 		errors++;
329 	}
330 	if (oldbad.bt_mbz != 0) {
331 		fprintf(stderr, "bad144: %s: bad magic number\n", name);
332 		errors++;
333 	}
334 	bt = oldbad.bt_bad;
335 	for (i = 0; i < 126; i++, bt++) {
336 		if (bt->bt_cyl == 0xffff && bt->bt_trksec == 0xffff)
337 			break;
338 		if ((bt->bt_cyl >= dp->d_ncylinders) ||
339 		    ((bt->bt_trksec >> 8) >= dp->d_ntracks) ||
340 		    ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) {
341 			fprintf(stderr,
342 		     "bad144: cyl/trk/sect out of range in existing entry: ");
343 			fprintf(stderr, "sn=%d, cn=%d, tn=%d, sn=%d\n",
344 				badsn(bt), bt->bt_cyl, bt->bt_trksec>>8,
345 				bt->bt_trksec & 0xff);
346 			errors++;
347 		}
348 		sn = (bt->bt_cyl * dp->d_ntracks +
349 		    (bt->bt_trksec >> 8)) *
350 		    dp->d_nsectors + (bt->bt_trksec & 0xff);
351 		if (i > 0 && sn < lsn && !warned) {
352 		    fprintf(stderr,
353 			"bad144: bad sector file is out of order\n");
354 		    errors++;
355 		    warned++;
356 		}
357 		if (i > 0 && sn == lsn) {
358 		    fprintf(stderr,
359 			"bad144: bad sector file contains duplicates (sn %d)\n",
360 			sn);
361 		    errors++;
362 		}
363 		lsn = sn;
364 	}
365 	if (errors)
366 		exit(1);
367 	return (i);
368 }
369 
370 /*
371  * Move the bad sector replacements
372  * to make room for the new bad sectors.
373  * new is the new number of bad sectors, old is the previous count.
374  */
375 shift(f, new, old)
376 {
377 	daddr_t repl;
378 
379 	/*
380 	 * First replacement is last sector of second-to-last track.
381 	 */
382 	repl = size - dp->d_nsectors - 1;
383 	new--; old--;
384 	while (new >= 0 && new != old) {
385 		if (old < 0 ||
386 		    compare(&curbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) {
387 			/*
388 			 * Insert new replacement here-- copy original
389 			 * sector if requested and possible,
390 			 * otherwise write a zero block.
391 			 */
392 			if (!copy ||
393 			    !blkcopy(f, badsn(&curbad.bt_bad[new]), repl - new))
394 				blkzero(f, repl - new);
395 		} else {
396 			if (blkcopy(f, repl - old, repl - new) == 0)
397 			    fprintf(stderr,
398 				"Can't copy replacement sector %d to %d\n",
399 				repl-old, repl-new);
400 			old--;
401 		}
402 		new--;
403 	}
404 }
405 
406 char *buf;
407 
408 /*
409  *  Copy disk sector s1 to s2.
410  */
411 blkcopy(f, s1, s2)
412 daddr_t s1, s2;
413 {
414 	register tries, n;
415 
416 	if (buf == (char *)NULL) {
417 		buf = malloc((unsigned)dp->d_secsize);
418 		if (buf == (char *)NULL) {
419 			fprintf(stderr, "Out of memory\n");
420 			exit(20);
421 		}
422 	}
423 	for (tries = 0; tries < RETRIES; tries++) {
424 		if (lseek(f, dp->d_secsize * s1, L_SET) < 0)
425 			Perror("lseek");
426 		if ((n = read(f, buf, dp->d_secsize)) == dp->d_secsize)
427 			break;
428 	}
429 	if (n != dp->d_secsize) {
430 		fprintf(stderr, "bad144: can't read sector, %d: ", s1);
431 		if (n < 0)
432 			perror((char *)0);
433 		return(0);
434 	}
435 	if (lseek(f, dp->d_secsize * s2, L_SET) < 0)
436 		Perror("lseek");
437 	if (verbose)
438 		printf("copying %d to %d\n", s1, s2);
439 	if (nflag == 0 && write(f, buf, dp->d_secsize) != dp->d_secsize) {
440 		fprintf(stderr,
441 		    "bad144: can't write replacement sector, %d: ", s2);
442 		perror((char *)0);
443 		return(0);
444 	}
445 	return(1);
446 }
447 
448 char *zbuf;
449 
450 blkzero(f, sn)
451 daddr_t sn;
452 {
453 
454 	if (zbuf == (char *)NULL) {
455 		zbuf = malloc((unsigned)dp->d_secsize);
456 		if (zbuf == (char *)NULL) {
457 			fprintf(stderr, "Out of memory\n");
458 			exit(20);
459 		}
460 	}
461 	if (lseek(f, dp->d_secsize * sn, L_SET) < 0)
462 		Perror("lseek");
463 	if (verbose)
464 		printf("zeroing %d\n", sn);
465 	if (nflag == 0 && write(f, zbuf, dp->d_secsize) != dp->d_secsize) {
466 		fprintf(stderr,
467 		    "bad144: can't write replacement sector, %d: ", sn);
468 		perror((char *)0);
469 	}
470 }
471 
472 compare(b1, b2)
473 register struct bt_bad *b1, *b2;
474 {
475 	if (b1->bt_cyl > b2->bt_cyl)
476 		return(1);
477 	if (b1->bt_cyl < b2->bt_cyl)
478 		return(-1);
479 	if (b1->bt_trksec == b2->bt_trksec)
480 		dups++;
481 	return (b1->bt_trksec - b2->bt_trksec);
482 }
483 
484 daddr_t
485 badsn(bt)
486 register struct bt_bad *bt;
487 {
488 	return ((bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors
489 		+ (bt->bt_trksec&0xff));
490 }
491 
492 #ifdef vax
493 #include <machine/dkio.h>
494 
495 struct rp06hdr {
496 	short	h_cyl;
497 	short	h_trksec;
498 	short	h_key1;
499 	short	h_key2;
500 	char	h_data[512];
501 #define	RP06_FMT	010000		/* 1 == 16 bit, 0 == 18 bit */
502 };
503 
504 /*
505  * Most massbus and unibus drives
506  * have headers of this form
507  */
508 struct hpuphdr {
509 	u_short	hpup_cyl;
510 	u_char	hpup_sect;
511 	u_char	hpup_track;
512 	char	hpup_data[512];
513 #define	HPUP_OKSECT	0xc000		/* this normally means sector is good */
514 #define	HPUP_16BIT	0x1000		/* 1 == 16 bit format */
515 };
516 int rp06format(), hpupformat();
517 
518 struct	formats {
519 	char	*f_name;		/* disk name */
520 	int	f_bufsize;		/* size of sector + header */
521 	int	f_bic;			/* value to bic in hpup_cyl */
522 	int	(*f_routine)();		/* routine for special handling */
523 } formats[] = {
524 	{ "rp06",	sizeof (struct rp06hdr), RP06_FMT,	rp06format },
525 	{ "eagle",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
526 	{ "capricorn",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
527 	{ "rm03",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
528 	{ "rm05",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
529 	{ "9300",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
530 	{ "9766",	sizeof (struct hpuphdr), HPUP_OKSECT,	hpupformat },
531 	{ 0, 0, 0, 0 }
532 };
533 
534 /*ARGSUSED*/
535 hpupformat(fp, dp, blk, buf, count)
536 	struct formats *fp;
537 	struct disklabel *dp;
538 	daddr_t blk;
539 	char *buf;
540 	int count;
541 {
542 	struct hpuphdr *hdr = (struct hpuphdr *)buf;
543 	int sect;
544 
545 	if (count < sizeof(struct hpuphdr)) {
546 		hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) |
547 			(blk / (dp->d_nsectors * dp->d_ntracks));
548 		sect = blk % (dp->d_nsectors * dp->d_ntracks);
549 		hdr->hpup_track = (u_char)(sect / dp->d_nsectors);
550 		hdr->hpup_sect = (u_char)(sect % dp->d_nsectors);
551 	}
552 	return (0);
553 }
554 
555 /*ARGSUSED*/
556 rp06format(fp, dp, blk, buf, count)
557 	struct formats *fp;
558 	struct disklabel *dp;
559 	daddr_t blk;
560 	char *buf;
561 	int count;
562 {
563 
564 	if (count < sizeof(struct rp06hdr)) {
565 		fprintf(stderr, "Can't read header on blk %d, can't reformat\n",
566 			blk);
567 		return (-1);
568 	}
569 	return (0);
570 }
571 
572 format(fd, blk)
573 	int fd;
574 	daddr_t blk;
575 {
576 	register struct formats *fp;
577 	static char *buf;
578 	static char bufsize;
579 	struct format_op fop;
580 	int n;
581 
582 	for (fp = formats; fp->f_name; fp++)
583 		if (strcmp(dp->d_typename, fp->f_name) == 0)
584 			break;
585 	if (fp->f_name == 0) {
586 		fprintf(stderr, "bad144: don't know how to format %s disks\n",
587 			dp->d_typename);
588 		exit(2);
589 	}
590 	if (buf && bufsize < fp->f_bufsize) {
591 		free(buf);
592 		buf = NULL;
593 	}
594 	if (buf == NULL)
595 		buf = malloc((unsigned)fp->f_bufsize);
596 	if (buf == NULL) {
597 		fprintf(stderr, "bad144: can't allocate sector buffer\n");
598 		exit(3);
599 	}
600 	bufsize = fp->f_bufsize;
601 	/*
602 	 * Here we do the actual formatting.  All we really
603 	 * do is rewrite the sector header and flag the bad sector
604 	 * according to the format table description.  If a special
605 	 * purpose format routine is specified, we allow it to
606 	 * process the sector as well.
607 	 */
608 	if (verbose)
609 		printf("format blk %d\n", blk);
610 	bzero((char *)&fop, sizeof(fop));
611 	fop.df_buf = buf;
612 	fop.df_count = fp->f_bufsize;
613 	fop.df_startblk = blk;
614 	bzero(buf, fp->f_bufsize);
615 	if (ioctl(fd, DIOCRFORMAT, &fop) < 0)
616 		perror("bad144: read format");
617 	if (fp->f_routine &&
618 	    (*fp->f_routine)(fp, dp, blk, buf, fop.df_count) != 0)
619 		return;
620 	if (fp->f_bic) {
621 		struct hpuphdr *xp = (struct hpuphdr *)buf;
622 
623 		xp->hpup_cyl &= ~fp->f_bic;
624 	}
625 	if (nflag)
626 		return;
627 	bzero((char *)&fop, sizeof(fop));
628 	fop.df_buf = buf;
629 	fop.df_count = fp->f_bufsize;
630 	fop.df_startblk = blk;
631 	if (ioctl(fd, DIOCWFORMAT, &fop) < 0)
632 		Perror("write format");
633 	if (fop.df_count != fp->f_bufsize) {
634 		char msg[80];
635 		(void)sprintf(msg, "bad144: write format %d", blk);
636 		perror(msg);
637 	}
638 }
639 #endif
640 
641 Perror(op)
642 	char *op;
643 {
644 
645 	fprintf(stderr, "bad144: "); perror(op);
646 	exit(4);
647 }
648