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