/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if defined(DOSCCS) && !defined(lint) char copyright[] = "@(#) Copyright (c) 1980 Regents of the University of California.\n\ All rights reserved.\n"; static char sccsid[] = "@(#)bad144.c 5.4.2 (2.11BSD GTE) 1/21/95"; #endif not lint /* * bad144 * * This program prints and/or initializes a bad block record for a pack, * in the format used by the DEC standard 144. * It can also add bad sector(s) to the record, moving the sector * replacements as necessary. * * It is preferable to write the bad information with a standard formatter, * but this program will do. * * RP06 sectors are marked as bad by inverting the format bit in the * header; on other drives the valid-sector bit is cleared. */ #include #include #include #include #include #include #include #define RETRIES 10 /* number of retries on reading old sectors */ int fflag, add, copy, verbose, nflag; int maxbad = MAXBAD; int compare(); union dkbbuf { struct dkbad bad; char buf[512]; } dkbbuf; #define dkbad dkbbuf.bad union dkbbuf oldbuf; #define oldbad oldbuf.bad daddr_t size, getold(), badsn(); struct disktab *dp; char name[BUFSIZ]; char *malloc(); off_t lseek(); long atol(); main(argc, argv) int argc; char *argv[]; { register struct bt_bad *bt; daddr_t sn, bn[126]; long bad; int i, f, nbad, new, errs; int toomany = 0; argc--, argv++; while (argc > 0 && **argv == '-') { (*argv)++; while (**argv) { switch (**argv) { case 'f': fflag++; break; case 'a': add++; break; case 'c': copy++; break; case 'v': verbose++; break; case 'n': nflag++; verbose++; break; } (*argv)++; } argc--, argv++; } if (argc < 2) { fprintf(stderr, "usage: bad144 [ -f ] type disk [ snum [ bn ... ] ]\n"); fprintf(stderr, "to read or overwrite bad-sector table, e.g.: bad144 rk07 hk0\n"); fprintf(stderr, "or bad144 -a [ -f ] [ -c ] type disk bn ...\n"); fprintf(stderr, "where options are:\n"); fprintf(stderr, "\t-a add new bad sectors to the table\n"); fprintf(stderr, "\t-f reformat listed sectors as bad\n"); fprintf(stderr, "\t-c copy original sector to replacement\n"); exit(1); } dp = getdiskbyname(argv[0]); if (dp == NULL) { fprintf(stderr, "%s: unknown disk type\n", argv[0]); exit(1); } size = (daddr_t)dp->d_nsectors * dp->d_ntracks * dp->d_ncylinders; #ifdef HACK printf("name \"%s\" type \"%s\" size %ld\n", dp->d_name, dp->d_type, size); printf("secsize %d ntracks %d nsectors %d ncylinders %d\n", dp->d_secsize, dp->d_ntracks, dp->d_nsectors, dp->d_ncylinders); printf("rpm %d badsectforw %d sectoffset %d\n", dp->d_rpm, dp->d_badsectforw, dp->d_sectoffset); for(i=0; i<8; i++) { printf(" %s%c size %ld bsize %d fsize %d\n", argv[1], "abcdefgh"[i], dp->d_partitions[i].p_size, dp->d_partitions[i].p_bsize, dp->d_partitions[i].p_fsize); } exit(0); #endif if (argv[1][0] != '/') #ifdef pdp11 (void)sprintf(name, "/dev/r%sh", argv[1]); #else (void)sprintf(name, "/dev/r%sc", argv[1]); #endif else (void)strcpy(name, argv[1]); argc -= 2; argv += 2; if (argc == 0) { f = open(name, O_RDONLY); if (f < 0) Perror(name); sn = getold(f, &dkbad); printf("bad block information at sector %ld in %s:\n", sn, name); printf("cartridge serial number: %ld(10)\n", dkbad.bt_csn); switch (dkbad.bt_flag) { case -1: printf("alignment cartridge\n"); break; case 0: break; default: printf("bt_flag=%x(16)?\n", dkbad.bt_flag); break; } bt = dkbad.bt_bad; for (i = 0; i < 126; i++) { bad = ((daddr_t)bt->bt_cyl<<16) + bt->bt_trksec; if (bad < 0) break; if (!toomany && i >= MAXBAD) { toomany++; printf("More bad sectors than system supports.\n"); printf("The remainder are not being replaced automatically...\n"); } printf("sn=%ld, cn=%d, tn=%d, sn=%d\n", badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff); bt++; } (void) checkold(); exit(0); } f = open(name, (fflag || add)? O_RDWR: O_WRONLY); if (f < 0) Perror(name); if (add) { /* * Read in the old badsector table. * Verify that it makes sense, and the bad sectors * are in order. Copy the old table to the new one. */ (void) getold(f, &oldbad); i = checkold(); if (verbose) printf("Had %d bad sectors\n", i); if (i + argc > maxbad) { printf("bad144: system not configured for %d more sectors\n", argc); printf("limited to %d by current configuration\n", maxbad); } else if (i + argc > 126) { printf("bad144: not enough room for %d more sectors\n", argc); printf("limited to 126 by information format\n"); exit(1); } dkbbuf = oldbuf; } else { dkbad.bt_csn = atol(*argv++); argc--; dkbad.bt_mbz = 0; if (argc > maxbad) { printf("bad144: system not configured for %d more sectors\n", argc); printf("limited to %d by current configuration\n", maxbad); } else if (argc > 126) { printf("bad144: too many bad sectors specified\n"); printf("limited to 126 by information format\n"); exit(1); } i = 0; } errs = 0; new = argc; while (argc > 0) { daddr_t sn = atol(*argv++); argc--; if (sn < 0 || sn >= size) { printf("%ld: out of range [0,%ld) for %s\n", sn, size, dp->d_name); errs++; continue; } bn[i] = sn; dkbad.bt_bad[i].bt_cyl = sn / (dp->d_nsectors*dp->d_ntracks); sn %= (dp->d_nsectors*dp->d_ntracks); dkbad.bt_bad[i].bt_trksec = ((sn/dp->d_nsectors) << 8) + (sn%dp->d_nsectors); i++; } if (errs) exit(1); nbad = i; while (i < 126) { dkbad.bt_bad[i].bt_trksec = -1; dkbad.bt_bad[i].bt_cyl = -1; i++; } if (add) { /* * Sort the new bad sectors into the list. * Then shuffle the replacement sectors so that * the previous bad sectors get the same replacement data. */ qsort((char *)dkbad.bt_bad, nbad, sizeof (struct bt_bad), compare); shift(f, nbad, nbad-new); } for (i = 0; i < 10 && i < dp->d_nsectors; i += 2) { if (lseek(f, (off_t)dp->d_secsize * (size - dp->d_nsectors + i), L_SET) < 0) Perror("lseek"); if (verbose) printf("write badsect file at %ld\n", size - dp->d_nsectors + i); if (nflag == 0 && write(f, (caddr_t)&dkbad, 512) != 512) { char msg[80]; (void)sprintf(msg, "bad144: write bad sector file %d", i/2); perror(msg); } } if (fflag) for (i = nbad - new; i < nbad; i++) format(f, bn[i]); exit(0); } daddr_t getold(f, bad) union dkbbuf *bad; { register int i; daddr_t sn; char msg[80]; for (i = 0; i < 10 && i < dp->d_nsectors; i += 2) { sn = size - dp->d_nsectors + i; if (lseek(f, (off_t)sn * dp->d_secsize, L_SET) < 0) Perror("lseek"); if (read(f, (char *)bad, 512) == 512) { if (i > 0) printf("Using bad-sector file %d\n", i/2); return(sn); } (void)sprintf(msg, "bad144: read bad sector file at sn %ld", sn); perror(msg); } fprintf(stderr, "bad144: %s: can't read bad block info\n", name); exit(1); /*NOTREACHED*/ } checkold() { register int i; register struct bt_bad *bt; daddr_t sn, lsn; int errors = 0, warned = 0; if (oldbad.bt_flag != 0) { fprintf(stderr, "bad144: %s: bad flag in bad-sector table\n", name); errors++; } if (oldbad.bt_mbz != 0) { fprintf(stderr, "bad144: %s: bad magic number\n", name); errors++; } lsn = 0L; bt = oldbad.bt_bad; for (i = 0; i < 126; i++, bt++) { if (bt->bt_cyl == -1 && bt->bt_trksec == -1) break; if ((bt->bt_cyl >= dp->d_ncylinders) || ((bt->bt_trksec >> 8) >= dp->d_ntracks) || ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) { fprintf(stderr, "bad144: cyl/trk/sect out of range in existing entry: "); fprintf(stderr, "sn=%ld, cn=%d, tn=%d, sn=%d\n", badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec & 0xff); errors++; } sn = (bt->bt_cyl * dp->d_ntracks + (bt->bt_trksec >> 8)) * dp->d_nsectors + (bt->bt_trksec & 0xff); if (sn < lsn && !warned) { fprintf(stderr, "bad144: bad sector file out of order\n"); errors++; warned++; } lsn = sn; } if (errors) exit(1); return (i); } /* * Move the bad sector replacements * to make room for the new bad sectors. * new is the new number of bad sectors, old is the previous count. */ shift(f, new, old) { daddr_t repl; /* * First replacement is last sector of second-to-last track. */ repl = size - dp->d_nsectors - 1; new--; old--; while (new >= 0 && new != old) { if (old < 0 || compare(&dkbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) { /* * Insert new replacement here-- copy original * sector if requested and possible, * otherwise write a zero block. */ if (!copy || !blkcopy(f, badsn(&dkbad.bt_bad[new]), repl - new)) blkzero(f, repl - new); } else { if (blkcopy(f, repl - old, repl - new) == 0) fprintf(stderr, "Can't copy replacement sector %d to %d\n", repl-old, repl-new); old--; } new--; } } char *buf; /* * Copy disk sector s1 to s2. */ blkcopy(f, s1, s2) daddr_t s1, s2; { register tries, n; if (buf == (char *)NULL) { buf = malloc((unsigned)dp->d_secsize); if (buf == (char *)NULL) { fprintf(stderr, "Out of memory\n"); exit(20); } } if (lseek(f, (off_t)dp->d_secsize * s1, L_SET) < 0) Perror("lseek"); for (tries = 0; tries < RETRIES; tries++) if ((n = read(f, buf, dp->d_secsize)) == dp->d_secsize) break; if (n != dp->d_secsize) { fprintf(stderr, "bad144: can't read sector, %ld: ", s1); if (n < 0) perror((char *)0); return(0); } if (lseek(f, (off_t)dp->d_secsize * s2, L_SET) < 0) Perror("lseek"); if (verbose) printf("copying %ld to %ld\n", s1, s2); if (nflag == 0 && write(f, buf, dp->d_secsize) != dp->d_secsize) { fprintf(stderr, "bad144: can't write replacement sector, %ld: ", s2); perror((char *)0); return(0); } return(1); } char *zbuf; blkzero(f, sn) daddr_t sn; { if (zbuf == (char *)NULL) { zbuf = malloc((unsigned)dp->d_secsize); if (zbuf == (char *)NULL) { fprintf(stderr, "Out of memory\n"); exit(20); } } if (lseek(f, (off_t)dp->d_secsize * sn, L_SET) < 0) Perror("lseek"); if (verbose) printf("zeroing %ld\n", sn); if (nflag == 0 && write(f, zbuf, dp->d_secsize) != dp->d_secsize) { fprintf(stderr, "bad144: can't write replacement sector, %ld: ", sn); perror((char *)0); } } compare(b1, b2) register struct bt_bad *b1, *b2; { if (b1->bt_cyl > b2->bt_cyl) return(1); if (b1->bt_cyl < b2->bt_cyl) return(-1); return (b1->bt_trksec - b2->bt_trksec); } daddr_t badsn(bt) register struct bt_bad *bt; { return (((daddr_t)bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors + (bt->bt_trksec&0xff)); } struct rp06hdr { short h_cyl; short h_trksec; short h_key1; short h_key2; char h_data[512]; #define RP06_FMT 010000 /* 1 == 16 bit, 0 == 18 bit */ }; /* * Most massbus and unibus drives * have headers of this form */ struct hpuphdr { u_short hpup_cyl; u_char hpup_sect; u_char hpup_track; char hpup_data[512]; #define HPUP_OKSECT 0xc000 /* this normally means sector is good */ #define HPUP_16BIT 0x1000 /* 1 == 16 bit format */ }; int rp06format(), hpupformat(); struct formats { char *f_name; /* disk name */ int f_bufsize; /* size of sector + header */ int f_bic; /* value to bic in hpup_cyl */ int (*f_routine)(); /* routine for special handling */ } formats[] = { { "rp06", sizeof (struct rp06hdr), RP06_FMT, rp06format }, { "eagle", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { "capricorn", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { "rm03", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { "rm05", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { "9300", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { "9766", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat }, { 0, 0, 0, 0 } }; /*ARGSUSED*/ hpupformat(fp, dp, blk, buf, count) struct formats *fp; struct disktab *dp; daddr_t blk; char *buf; int count; { struct hpuphdr *hdr = (struct hpuphdr *)buf; int sect; if (count < sizeof(struct hpuphdr)) { hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) | (blk / (dp->d_nsectors * dp->d_ntracks)); sect = blk % (dp->d_nsectors * dp->d_ntracks); hdr->hpup_track = (u_char)(sect / dp->d_nsectors); hdr->hpup_sect = (u_char)(sect % dp->d_nsectors); } return (0); } /*ARGSUSED*/ rp06format(fp, dp, blk, buf, count) struct formats *fp; struct disktab *dp; daddr_t blk; char *buf; int count; { if (count < sizeof(struct rp06hdr)) { fprintf(stderr, "Can't read header on blk %ld, can't reformat\n", blk); return (-1); } return (0); } format(fd, blk) int fd; daddr_t blk; { register struct formats *fp; static char *buf; static char bufsize; int n; for (fp = formats; fp->f_name; fp++) if (strcmp(dp->d_name, fp->f_name) == 0) break; if (fp->f_name == 0) { fprintf(stderr, "bad144: don't know how to format %s disks\n", dp->d_name); exit(2); } if (buf && bufsize < fp->f_bufsize) { free(buf); buf = NULL; } if (buf == NULL) buf = malloc((unsigned)fp->f_bufsize); if (buf == NULL) { fprintf(stderr, "bad144: can't allocate sector buffer\n"); exit(3); } bufsize = fp->f_bufsize; /* * Here we do the actual formatting. All we really * do is rewrite the sector header and flag the bad sector * according to the format table description. If a special * purpose format routine is specified, we allow it to * process the sector as well. */ if (lseek(fd, (off_t)blk * dp->d_secsize, L_SET) < 0) Perror("lseek"); if (verbose) printf("format blk %ld\n", blk); if (ioctl(fd, DKIOCHDR, (char *)0) < 0) Perror("ioctl DKIOCHDR 1"); if ((n = read(fd, buf, fp->f_bufsize)) < 0) bzero(buf, fp->f_bufsize); if (fp->f_bic) { struct hpuphdr *xp = (struct hpuphdr *)buf; xp->hpup_cyl &= ~fp->f_bic; } if (fp->f_routine) if ((*fp->f_routine)(fp, dp, blk, buf, n) != 0) return; if (lseek(fd, (off_t)blk * dp->d_secsize, L_SET) < 0) Perror("lseek"); if (nflag) return; if (ioctl(fd, DKIOCHDR, (char *)0) < 0) Perror("ioctl DKIOCHDR 2"); if (write(fd, buf, fp->f_bufsize) != fp->f_bufsize) { char msg[80]; (void)sprintf(msg, "bad144: write format %d", blk); perror(msg); } } Perror(op) char *op; { fprintf(stderr, "bad144: "); perror(op); exit(4); }