1 /* $NetBSD: bad144.c,v 1.26 2008/07/21 13:36:57 lukem Exp $ */
4 * Copyright (c) 1980, 1986, 1988, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1988, 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)bad144.c 8.2 (Berkeley) 4/27/95";
42 __RCSID("$NetBSD: bad144.c,v 1.26 2008/07/21 13:36:57 lukem Exp $");
49 * This program prints and/or initializes a bad block record for a pack,
50 * in the format used by the DEC standard 144.
51 * It can also add bad sector(s) to the record, moving the sector
52 * replacements as necessary.
54 * It is preferable to write the bad information with a standard formatter,
55 * but this program will do.
57 * RP06 sectors are marked as bad by inverting the format bit in the
58 * header; on other drives the valid-sector bit is cleared.
60 #include <sys/param.h>
61 #include <sys/dkbad.h>
62 #include <sys/ioctl.h>
64 #include <sys/disklabel.h>
65 #include <ufs/ufs/dinode.h>
66 #include <ufs/ffs/fs.h>
76 #define RETRIES 10 /* number of retries on reading old sectors */
81 int add
, copy
, verbose
, nflag
;
83 int badfile
= -1; /* copy of badsector table to use, -1 if any */
84 #define MAXSECSIZE 1024
85 struct dkbad curbad
, oldbad
;
86 #define DKBAD_MAGIC 0x4321
90 struct disklabel label
;
91 char diskname
[MAXPATHLEN
];
93 daddr_t
badsn(const struct bt_bad
*);
94 int blkcopy(int, daddr_t
, daddr_t
);
95 void blkzero(int, daddr_t
);
97 int compare(const void *, const void *);
98 daddr_t
getold(int, struct dkbad
*);
99 int main(int, char **);
100 void shift(int, int, int);
104 #define OPTSTRING "01234acfvn"
106 #define OPTSTRING "01234acvn"
110 main(int argc
, char *argv
[])
113 daddr_t sn
, bn
[NBT_BAD
];
114 int i
, f
, nbad
, new, bad
, errs
, ch
;
116 while ((ch
= getopt(argc
, argv
, OPTSTRING
)) != -1) {
154 f
= opendisk(argv
[0], argc
== 1 ? O_RDONLY
: O_RDWR
, diskname
,
155 sizeof(diskname
), 0);
157 err(4, "opendisk `%s'", diskname
);
158 /* obtain label and adjust to fit */
160 if (ioctl(f
, DIOCGDINFO
, dp
) < 0)
161 err(4, "ioctl DIOCGDINFO `%s'", diskname
);
162 if (dp
->d_magic
!= DISKMAGIC
|| dp
->d_magic2
!= DISKMAGIC
163 /* dkcksum(lp) != 0 */ )
164 errx(1, "Bad pack magic number (pack is unlabeled)");
165 if (dp
->d_secsize
> MAXSECSIZE
|| dp
->d_secsize
== 0)
166 errx(7, "Disk sector size too large/small (%d)",
169 if (dp
->d_type
== DTYPE_SCSI
)
170 errx(1, "SCSI disks don't use bad144!");
171 /* are we inside a DOS partition? */
172 if (dp
->d_partitions
[0].p_offset
) {
173 /* yes, rules change. assume bad tables at end of partition C,
174 which maps all of DOS partition we are within -wfj */
175 size
= dp
->d_partitions
[2].p_offset
+ dp
->d_partitions
[2].p_size
;
178 size
= dp
->d_nsectors
* dp
->d_ntracks
* dp
->d_ncylinders
;
182 sn
= getold(f
, &oldbad
);
183 printf("bad block information at sector %lld in %s:\n",
184 (long long)sn
, diskname
);
185 printf("cartridge serial number: %d(10)\n", oldbad
.bt_csn
);
186 switch (oldbad
.bt_flag
) {
189 printf("alignment cartridge\n");
196 printf("bt_flag=%x(16)?\n", oldbad
.bt_flag
);
200 for (i
= 0; i
< NBT_BAD
; i
++) {
201 bad
= (bt
->bt_cyl
<<16) + bt
->bt_trksec
;
204 printf("sn=%lld, cn=%d, tn=%d, sn=%d\n",
205 (long long)badsn(bt
),
206 bt
->bt_cyl
, bt
->bt_trksec
>>8, bt
->bt_trksec
&0xff);
214 * Read in the old badsector table.
215 * Verify that it makes sense, and the bad sectors
216 * are in order. Copy the old table to the new one.
218 (void) getold(f
, &oldbad
);
221 printf("Had %d bad sectors, adding %d\n", i
, argc
);
222 if (i
+ argc
> NBT_BAD
) {
223 printf("bad144: not enough room for %d more sectors\n",
225 printf("limited to %d by information format\n",
231 curbad
.bt_csn
= atoi(*argv
++);
234 curbad
.bt_flag
= DKBAD_MAGIC
;
235 if (argc
> NBT_BAD
) {
236 printf("bad144: too many bad sectors specified\n");
237 printf("limited to %d by information format\n",
248 if (sn
< 0 || sn
>= size
) {
249 printf("%lld: out of range [0,%lld) for disk %s\n",
250 (long long)sn
, (long long)size
, dp
->d_typename
);
255 curbad
.bt_bad
[i
].bt_cyl
= sn
/ (dp
->d_nsectors
*dp
->d_ntracks
);
256 sn
%= (dp
->d_nsectors
*dp
->d_ntracks
);
257 curbad
.bt_bad
[i
].bt_trksec
=
258 ((sn
/dp
->d_nsectors
) << 8) + (sn
%dp
->d_nsectors
);
264 while (i
< NBT_BAD
) {
265 curbad
.bt_bad
[i
].bt_trksec
= -1;
266 curbad
.bt_bad
[i
].bt_cyl
= -1;
271 * Sort the new bad sectors into the list.
272 * Then shuffle the replacement sectors so that
273 * the previous bad sectors get the same replacement data.
275 qsort((char *)curbad
.bt_bad
, nbad
, sizeof (struct bt_bad
),
278 errx(3, "bad sectors have been duplicated; "
279 "can't add existing sectors");
280 shift(f
, nbad
, nbad
-new);
286 for (; i
< 10 && i
< (int)dp
->d_nsectors
; i
+= 2) {
288 (off_t
)(dp
->d_secsize
* (size
- dp
->d_nsectors
+ i
)),
292 printf("write badsect file at %lld\n",
293 (long long)size
- dp
->d_nsectors
+ i
);
294 if (nflag
== 0 && write(f
, (caddr_t
)&curbad
, sizeof(curbad
)) !=
296 err(4, "write bad sector file %d", i
/2);
301 if (nflag
== 0 && fflag
)
302 for (i
= nbad
- new; i
< nbad
; i
++)
306 if (nflag
== 0 && ioctl(f
, DIOCSBAD
, (caddr_t
)&curbad
) < 0)
307 warnx("Can't sync bad-sector file; reboot for changes "
310 if ((dp
->d_flags
& D_BADSECT
) == 0 && nflag
== 0) {
311 dp
->d_flags
|= D_BADSECT
;
312 if (ioctl(f
, DIOCWDINFO
, dp
) < 0) {
315 "Can't write label to enable bad sector handling");
322 getold(int f
, struct dkbad
*bad
)
331 for (; i
< 10 && i
< (int)dp
->d_nsectors
; i
+= 2) {
332 sn
= size
- dp
->d_nsectors
+ i
;
333 if (lseek(f
, (off_t
)(sn
* dp
->d_secsize
), SEEK_SET
) < 0)
335 if ((size_t)read(f
, (char *) bad
, dp
->d_secsize
) == dp
->d_secsize
) {
337 printf("Using bad-sector file %d\n", i
/2);
340 warn("read bad sector file at sn %lld", (long long)sn
);
344 errx(1, "%s: can't read bad block info", diskname
);
354 int errors
= 0, warned
= 0;
357 if (oldbad
.bt_flag
!= DKBAD_MAGIC
) {
358 warnx("%s: bad flag in bad-sector table", diskname
);
361 if (oldbad
.bt_mbz
!= 0) {
362 warnx("%s: bad magic number", diskname
);
366 for (i
= 0; i
< NBT_BAD
; i
++, bt
++) {
367 if (bt
->bt_cyl
== 0xffff && bt
->bt_trksec
== 0xffff)
369 if ((bt
->bt_cyl
>= dp
->d_ncylinders
) ||
370 ((bt
->bt_trksec
>> 8) >= dp
->d_ntracks
) ||
371 ((bt
->bt_trksec
& 0xff) >= dp
->d_nsectors
)) {
372 warnx("cyl/trk/sect out of range in existing entry: "
373 "sn=%lld, cn=%d, tn=%d, sn=%d",
374 (long long)badsn(bt
), bt
->bt_cyl
, bt
->bt_trksec
>>8,
375 bt
->bt_trksec
& 0xff);
378 sn
= (bt
->bt_cyl
* dp
->d_ntracks
+
379 (bt
->bt_trksec
>> 8)) *
380 dp
->d_nsectors
+ (bt
->bt_trksec
& 0xff);
381 if (i
> 0 && sn
< lsn
&& !warned
) {
382 warnx("bad sector file is out of order");
386 if (i
> 0 && sn
== lsn
) {
387 warnx("bad sector file contains duplicates (sn %lld)",
399 * Move the bad sector replacements
400 * to make room for the new bad sectors.
401 * new is the new number of bad sectors, old is the previous count.
404 shift(int f
, int new, int old
)
409 * First replacement is last sector of second-to-last track.
411 repl
= size
- dp
->d_nsectors
- 1;
413 while (new >= 0 && new != old
) {
415 compare(&curbad
.bt_bad
[new], &oldbad
.bt_bad
[old
]) > 0) {
417 * Insert new replacement here-- copy original
418 * sector if requested and possible,
419 * otherwise write a zero block.
422 !blkcopy(f
, badsn(&curbad
.bt_bad
[new]), repl
- new))
423 blkzero(f
, repl
- new);
425 if (blkcopy(f
, repl
- old
, repl
- new) == 0)
426 warnx("Can't copy replacement sector %lld to %lld",
427 (long long)repl
-old
, (long long)repl
-new);
437 * Copy disk sector s1 to s2.
440 blkcopy(int f
, daddr_t s1
, daddr_t s2
)
444 if (buf
== (char *)NULL
) {
445 buf
= malloc((unsigned)dp
->d_secsize
);
446 if (buf
== (char *)NULL
)
447 errx(20, "Out of memory");
449 for (tries
= 0; tries
< RETRIES
; tries
++) {
450 if (lseek(f
, (off_t
)(dp
->d_secsize
* s1
), SEEK_SET
) < 0)
452 if ((size_t)(n
= read(f
, buf
, dp
->d_secsize
)) == dp
->d_secsize
)
455 if ((size_t)n
!= dp
->d_secsize
) {
457 err(4, "can't read sector, %lld", (long long)s1
);
459 errx(4, "can't read sector, %lld", (long long)s1
);
462 if (lseek(f
, (off_t
)(dp
->d_secsize
* s2
), SEEK_SET
) < 0)
465 printf("copying %lld to %lld\n", (long long)s1
, (long long)s2
);
466 if (nflag
== 0 && (size_t)write(f
, buf
, dp
->d_secsize
) != dp
->d_secsize
) {
467 warn("can't write replacement sector, %lld", (long long)s2
);
474 blkzero(int f
, daddr_t sn
)
478 zbuf
= calloc(1, (unsigned int)dp
->d_secsize
);
480 errx(20, "Out of memory");
481 if (lseek(f
, (off_t
)(dp
->d_secsize
* sn
), SEEK_SET
) < 0) {
486 printf("zeroing %lld\n", (long long)sn
);
487 if (nflag
== 0 && (size_t)write(f
, zbuf
, dp
->d_secsize
) != dp
->d_secsize
)
488 warn("can't write replacement sector, %lld",
494 compare(const void *v1
, const void *v2
)
496 const struct bt_bad
*b1
= v1
;
497 const struct bt_bad
*b2
= v2
;
499 if (b1
->bt_cyl
> b2
->bt_cyl
)
501 if (b1
->bt_cyl
< b2
->bt_cyl
)
503 if (b1
->bt_trksec
== b2
->bt_trksec
)
505 return (b1
->bt_trksec
- b2
->bt_trksec
);
509 badsn(const struct bt_bad
*bt
)
512 return ((bt
->bt_cyl
* dp
->d_ntracks
513 + (bt
->bt_trksec
>> 8)) * dp
->d_nsectors
514 + (bt
->bt_trksec
& 0xff));
525 #define RP06_FMT 010000 /* 1 == 16 bit, 0 == 18 bit */
529 * Most massbus and unibus drives
530 * have headers of this form
537 #define HPUP_OKSECT 0xc000 /* this normally means sector is good */
538 #define HPUP_16BIT 0x1000 /* 1 == 16 bit format */
540 int rp06format(struct formats
*, struct disklabel
*, daddr_t
, char *, int);
541 int hpupformat(struct formats
*, struct disklabel
*, daddr_t
, char *, int);
544 char *f_name
; /* disk name */
545 int f_bufsize
; /* size of sector + header */
546 int f_bic
; /* value to bic in hpup_cyl */
547 int (*f_routine
)(); /* routine for special handling */
549 { "rp06", sizeof (struct rp06hdr
), RP06_FMT
, rp06format
},
550 { "eagle", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
551 { "capricorn", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
552 { "rm03", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
553 { "rm05", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
554 { "9300", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
555 { "9766", sizeof (struct hpuphdr
), HPUP_OKSECT
, hpupformat
},
561 hpupformat(struct formats
*fp
, struct disklabel
*dp
, daddr_t blk
, char *buf
,
564 struct hpuphdr
*hdr
= (struct hpuphdr
*)buf
;
567 if (count
< sizeof(struct hpuphdr
)) {
568 hdr
->hpup_cyl
= (HPUP_OKSECT
| HPUP_16BIT
) |
569 (blk
/ (dp
->d_nsectors
* dp
->d_ntracks
));
570 sect
= blk
% (dp
->d_nsectors
* dp
->d_ntracks
);
571 hdr
->hpup_track
= (u_char
)(sect
/ dp
->d_nsectors
);
572 hdr
->hpup_sect
= (u_char
)(sect
% dp
->d_nsectors
);
579 rp06format(struct formats
*fp
, struct disklabel
*dp
, daddr_t blk
, char *buf
,
583 if (count
< sizeof(struct rp06hdr
)) {
584 warnx("Can't read header on blk %d, can't reformat", blk
);
591 format(int fd
, daddr_t blk
)
596 struct format_op fop
;
599 for (fp
= formats
; fp
->f_name
; fp
++)
600 if (strcmp(dp
->d_typename
, fp
->f_name
) == 0)
603 errx(2, "don't know how to format %s disks", dp
->d_typename
);
604 if (buf
&& bufsize
< fp
->f_bufsize
) {
609 buf
= malloc((unsigned)fp
->f_bufsize
);
611 errx(3, "can't allocate sector buffer");
612 bufsize
= fp
->f_bufsize
;
614 * Here we do the actual formatting. All we really
615 * do is rewrite the sector header and flag the bad sector
616 * according to the format table description. If a special
617 * purpose format routine is specified, we allow it to
618 * process the sector as well.
621 printf("format blk %d\n", blk
);
622 memset((char *)&fop
, 0, sizeof(fop
));
624 fop
.df_count
= fp
->f_bufsize
;
625 fop
.df_startblk
= blk
;
626 memset(buf
, 0, fp
->f_bufsize
);
627 if (ioctl(fd
, DIOCRFORMAT
, &fop
) < 0)
630 (*fp
->f_routine
)(fp
, dp
, blk
, buf
, fop
.df_count
) != 0)
633 struct hpuphdr
*xp
= (struct hpuphdr
*)buf
;
635 xp
->hpup_cyl
&= ~fp
->f_bic
;
639 memset((char *)&fop
, 0, sizeof(fop
));
641 fop
.df_count
= fp
->f_bufsize
;
642 fop
.df_startblk
= blk
;
643 if (ioctl(fd
, DIOCWFORMAT
, &fop
) < 0)
644 err(4, "write format");
645 if (fop
.df_count
!= fp
->f_bufsize
)
646 warn("write format %d", blk
);
654 fprintf(stderr
, "usage: bad144 [-%sv] disk [sno [bad ...]]\n"
655 "to read or overwrite the bad-sector table, e.g.: bad144 hp0\n"
656 "or bad144 -a [-c%sv] disk [bad ...]\n"
657 "where options are:\n"
658 "\t-a add new bad sectors to the table\n"
659 "\t-c copy original sector to replacement\n"
661 "\t-v verbose mode\n",
663 "f", "f", "\t-f reformat listed sectors as bad\n"