Subject: sdremap.c
To: None <phil@cs.wwu.edu>
From: Jason Thorpe <thorpej@nas.nasa.gov>
List: current-users
Date: 04/01/1996 20:41:07
I remember a time ago when you [Phil] needed a program to remap a bad block 
on a SCSI disk on www.netbsd.org ... Well, I had forgotten about it until 
just now ... I wrote a program to do just this for a friend of mine who 
is running 1.1 on a PC, just last week...

It's a quick hack, so don't laugh, but just in case anyone might find it 
useful in the future ... it appeared to work on Andy's PC, so...

It needs some frobbing to work under -current (SCSI structure changes)...

Share and enjoy...

--------------------------------------------------------------------------
Jason R. Thorpe                                       thorpej@nas.nasa.gov
NASA Ames Research Center                               Home: 408.866.1912
NAS: M/S 258-6                                          Work: 415.604.0935
Moffett Field, CA 94035                                Pager: 415.428.6939

----- snip -----
/*
 * sdremap <dev> <blkno> - tell a SCSI disk to remap the bad block
 *                         specified on the command line.
 *
 * Compile this on your favorite NetBSD 1.1 system with:
 *
 *	cc -I/sys -o sdremap sdremap.c
 *
 * When you use this program, make sure the disk you're mucking with
 * has no mounted filesystems (unless that's just not possible, in which
 * case, make sure they're mounted read-only, and you're in single-user
 * mode).  You must use the "raw partition" of the disk.  On an i386, this
 * is `d', and everywhere else `c'.
 *
 * I.e.:
 *	./sdremap /dev/rsd1d 356642
 *
 * Written by Jason R. Thorpe <thorpej@NetBSD.ORG>.
 * This program is in the public domain.
 *
 * Use this software at your own risk.  At no time shall Jason R. Thorpe
 * be liable for any damages it may cause.
 *
 * TODO:
 *	Handle multiple blocks with a single command.  I'm just lazy.
 */

#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/scsiio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>

extern	char *__progname;		/* from crt0.o */

static	void usage __P((void));
static	void cleanup __P((void));

static	int disk_fd;

int
main(argc, argv)
	int argc;
	char **argv;
{
	struct scsi_reassign_blocks cmd;
	struct scsi_reassign_blocks_data desc;
	scsireq_t req;
	char *disk, *cp;
	u_int32_t blkno;
	u_int16_t desc_data_len;

	if (argc != 3)
		usage();

	disk = argv[1];
	blkno = (u_int32_t)strtol(argv[2], &cp, 10);
	if (*cp != '\0')
		errx(1, "invalid block number `%s'", argv[2]);

	/* Register cleanup function. */
	if (atexit(cleanup))
		err(1, "can't register cleanup function");

	/* Open the device... */
	if ((disk_fd = open(disk, O_RDWR, 0600)) == -1)
		err(1, "%s: open", disk);

	/* Build SCSI command. */
	bzero(&cmd, sizeof(cmd));
	cmd.opcode = REASSIGN_BLOCKS;

	/*
	 * Build the block descriptor.
	 */
	desc_data_len = sizeof(desc.defect_descriptor[0]);
	bzero(&desc, sizeof(desc));
	/* Descriptor length. */
	desc.length_msb = (desc_data_len >> 8) & 0xff;
	desc.length_lsb = desc_data_len & 0xff;
	/* Block number. */
	desc.defect_descriptor[0].dlbaddr_3 = (blkno >> 24) & 0xff;
	desc.defect_descriptor[0].dlbaddr_2 = (blkno >> 16) & 0xff;
	desc.defect_descriptor[0].dlbaddr_1 = (blkno >> 8) & 0xff;
	desc.defect_descriptor[0].dlbaddr_0 = blkno & 0xff;

	/* Fill out user-level SCSI request. */
	bzero(&req, sizeof(req));
	bcopy((struct scsi_generic *)&cmd, &req.cmd, sizeof(cmd));
	req.cmdlen = (u_char)sizeof(cmd);
	req.databuf = (void *)&desc;
	req.datalen = (u_long)sizeof(desc);
	req.flags |= SCCMD_WRITE;
	req.timeout = 20000;			/* XXX */

	/* Send the request to the SCSI subsystem. */
	if (ioctl(disk_fd, SCIOCCOMMAND, (char *)&req) == -1)
		err(1, "SCIOCCOMMAND");

	/*
	 * XXX should check for error condition and print
	 * sense data.
	 */

	exit(0);
}

static void
cleanup()
{

	/* ...simple enough... */
	(void)close(disk_fd);
}

static void
usage()
{

	fprintf(stderr, "usage: %s disk blkno\n", __progname);
	exit(1);
}