diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/fdread/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/fdread/fdread.1 | 184 | ||||
-rw-r--r-- | usr.sbin/fdread/fdread.c | 307 |
3 files changed, 497 insertions, 0 deletions
diff --git a/usr.sbin/fdread/Makefile b/usr.sbin/fdread/Makefile new file mode 100644 index 0000000..8d62a43 --- /dev/null +++ b/usr.sbin/fdread/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= fdread +CFLAGS+=-Wall + +.include <bsd.prog.mk> diff --git a/usr.sbin/fdread/fdread.1 b/usr.sbin/fdread/fdread.1 new file mode 100644 index 0000000..7e21404 --- /dev/null +++ b/usr.sbin/fdread/fdread.1 @@ -0,0 +1,184 @@ +.\" +.\" Copyright (c) 2001 Joerg Wunsch +.\" +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.\" +.Dd May 14, 2001 +.Os FreeBSD +.Dt FDREAD 1 +.Sh NAME +.Nm fdread +.Nd read floppy disks +.Sh SYNOPSIS +.Nm +.Op Fl q +.Op Fl r +.Op Fl d Ar device +.Op Fl f Ar fillbyte +.Op Fl o Ar file +.Sh DESCRIPTION +.Nm Fdread +reads floppy disks. Effective read blocking based on the track +size is performed, and floppy-specific error recovery of otherwise +bad blocks can be enabled. +.Pp +.Nm Fdread +will always read an entire floppy medium, and write its contents to +the respective output file. Unlike other tools like +.Xr dd 1 , +.Nm +automatically uses a read block size that is more efficient than +reading single blocks (usually one track of data at a time), but +falls back to reading single floppy sectors in case of an input/output +error occurred, in order to obtain as much valid data as possible. +While +.Nm +is working, kernel error reporting for floppy errors is turned off, so +the console and/or syslog are not flooded with kernel error messages. +.Sh Options +The options are as follows: +.Bl -tag -width XXXX -offset indent +.It Fl q +Turn on quiet mode. By default, the medium parameters of the device +are being written to standard error output, progress will be indicated +by the approximate number of kilobytes read so far, and errors will be +printed out in detail, including the information about the location of +recovered data in the output. In quiet mode, none of these messages +will be generated. +.It Fl r +Enable error recovery. By default, +.Nm +stops after the first unrecovered read error, much like +.Xr dd 1 +does. In recovery mode, however, one of two recovery actions will be +taken: +.Bl -bullet -offset indent +.It +If the error was a CRC error in the data field, the +kernel is told to ignore the error, and data are transferred to the +output file anyway. +.Em Note that this will cause the erroneous data +.Em to be included in the output file ! +Still, this is the best recovery action that can be taken at all. +.It +All other errors are really fatal (usually, the FDC didn't find the +sector ID fields), thus a dummy block with fill +bytes will be included in the output file. +.El +.Pp +Unless operating in quiet mode, the action taken and the location of +the error in the output file will be displayed. +.It Fl d Ar device +Specify the input floppy device, defaulting to +.Pa /dev/fd0 . +The parameter +.Ar device +must be a valid floppy disk device. +.It Fl f Ar fillbyte +Value of the fill byte used for dummy blocks in the output file in +recovery mode. Defaults to +.Ql 0xf0 . +(Mnemonic: +.Dq foo . ) +The value can be specified using the usual C language notation of +the number base. +.It Fl o Ar file +Specify the output file to be +.Ar file . +By default, the data will be written to standard output. +.El +.Sh FILES +.Bl -tag -width XXXXXXXX -offset indent +.It Pa /dev/fd0 +Default device to read from. +.Sh DIAGNOSTICS +.Nm Fdread +sets the exit value according to +.Xr sysexits 3 . +In recovery mode, the exit value will be set to +.Sy EX_IOERR +if any error occurred during processing (even in quiet mode). +.Pp +Unless running in quiet mode, upon encountering an error, the status +of the floppy disc controller (FDC) will be printed out, both in +hexadecimal form, followed by a textual description that translates +those values into a human-readable form for the most common error +cases that can happen in a PC environment. +.Pp +The FDC error status includes the three FDC status registers +.Ql ST0 , +.Ql ST1 , +and +.Ql ST2 , +as well as the location of the error (physical cylinder, head, and sector +number, plus the +.Dq sector shift value , +respectively). See the manual for the NE765 or compatible for details +about the status register contents. +.Pp +The FDC's status is then examined to determine whether the error is +deemed to be recoverable. If error recovery was requested, the +location of the bad block in the output file is indicated by its +(hexadecimal) bounds. Also, a summary line indicating the total number +of transfer errors will be printed before exiting. +.Sh SEE ALSO +.Xr dd 1 , +.Xr fdc 4 , +.Xr fdcontrol 8 , +.Xr fdwrite 1 , +.Xr sysexits 3 +.Sh HISTORY +.Nm Fdread +was written mainly to provide a means of recovering at least some of +the data on bad media, and to obviate the need to invoke +.Xr dd 1 +with too many hard to memorize options that might be useful to handle +a floppy. +.Pp +The command appeared in +.Fx 5.0 . +.Sh AUTHORS +Program and man page by +.ie t J\(:org Wunsch. +.el Joerg Wunsch. +.Sh BUGS +Concurrent traffic on the second floppy drive located at the same FDC +will make error recovery attempts pointless, since the FDC status +obtained after a read error occurred cannot be guaranteed to actually +belong to the erroneous transfer. Thus using option +.Fl r +is only reliable if +.Ar device +is the only active drive on that controller. +.Pp +No attempt beyond the floppy error retry mechanism of +.Xr fdc 4 +is made in order to see whether bad sectors could still be read +without errors by trying multiple times. +.Pp +Bits that are (no longer) available on the floppy medium cannot be +guessed by +.Nm fdread . diff --git a/usr.sbin/fdread/fdread.c b/usr.sbin/fdread/fdread.c new file mode 100644 index 0000000..bb75841 --- /dev/null +++ b/usr.sbin/fdread/fdread.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2001 Joerg Wunsch + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <machine/ioctl_fd.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +/* XXX -- header file should probably be installed somewhere */ +#include "/sys/isa/ic/nec765.h" + +int quiet, recover; +unsigned char fillbyte = 0xf0; /* "foo" */ + +int doread(int fd, FILE *of, const char *devname); +void printstatus(struct fdc_status *fdcsp); +void usage(void); + +void +usage(void) +{ + + errx(EX_USAGE, + "usage: fdread [-qr] [-d device] [-f fillbyte] [-o file]"); +} + + +int +main(int argc, char **argv) +{ + int c, errs = 0; + const char *fname = 0, *devname = "/dev/fd0"; + char *cp; + FILE *of = stdout; + int fd; + unsigned long ul; + + while ((c = getopt(argc, argv, "d:f:o:qr")) != -1) + switch (c) { + case 'd': + devname = optarg; + break; + + case 'f': + ul = strtoul(optarg, &cp, 0); + if (*cp != '\0') { + fprintf(stderr, + "Bad argument %s to -f option; must be numeric\n", + optarg); + usage(); + } + if (ul > 0xff) + warnx( + "Warning: fillbyte %#lx too large, truncating\n", + ul); + fillbyte = ul & 0xff; + break; + + case 'o': + fname = optarg; + break; + + case 'q': + quiet++; + break; + + case 'r': + recover++; + break; + + default: + errs++; + } + argc -= optind; + argv += optind; + + if (argc != 0 || errs) + usage(); + + if (fname) { + if ((of = fopen(fname, "w")) == NULL) + err(EX_OSERR, "cannot create output file %s", fname); + } + + if ((fd = open(devname, O_RDONLY)) == -1) + err(EX_OSERR, "cannot open device %s", devname); + + return (doread(fd, of, devname)); +} + +int +doread(int fd, FILE *of, const char *devname) +{ + char *trackbuf; + int rv, fdopts, recoverable, nerrs = 0; + unsigned int nbytes, tracksize, mediasize, secsize, n; + struct fdc_status fdcs; + struct fd_type fdt; + + if (ioctl(fd, FD_GTYPE, &fdt) == -1) + err(EX_OSERR, "ioctl(FD_GTYPE) failed -- not a floppy?"); + fdopts = FDOPT_NOERRLOG; + if (ioctl(fd, FD_SOPTS, &fdopts) == -1) + err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)"); + + secsize = 128 << fdt.secsize; + tracksize = fdt.sectrac * secsize; + mediasize = tracksize * fdt.tracks * fdt.heads; + if ((trackbuf = malloc(tracksize)) == 0) + errx(EX_TEMPFAIL, "out of memory"); + + if (!quiet) + fprintf(stderr, "Reading %d * %d * %d * %d medium at %s\n", + fdt.tracks, fdt.heads, fdt.sectrac, secsize, devname); + + for (nbytes = 0; nbytes < mediasize;) { + if (lseek(fd, nbytes, SEEK_SET) != nbytes) + err(EX_OSERR, "cannot lseek()"); + rv = read(fd, trackbuf, tracksize); + if (rv == 0) { + /* EOF? */ + warnx("premature EOF after %u bytes", nbytes); + return (EX_OK); + } + if (rv == tracksize) { + nbytes += rv; + if (!quiet) + fprintf(stderr, "%5d KB\r", nbytes / 1024); + fwrite(trackbuf, sizeof(unsigned char), rv, of); + fflush(of); + continue; + } + if (rv < tracksize) { + /* should not happen */ + nbytes += rv; + if (!quiet) + fprintf(stderr, "\nshort after %5d KB\r", + nbytes / 1024); + fwrite(trackbuf, sizeof(unsigned char), rv, of); + fflush(of); + continue; + } + if (rv == -1) { + /* fall back reading one sector at a time */ + for (n = 0; n < tracksize; n += secsize) { + if (lseek(fd, nbytes, SEEK_SET) != nbytes) + err(EX_OSERR, "cannot lseek()"); + rv = read(fd, trackbuf, secsize); + if (rv == secsize) { + nbytes += rv; + if (!quiet) + fprintf(stderr, "%5d KB\r", + nbytes / 1024); + fwrite(trackbuf, sizeof(unsigned char), + rv, of); + fflush(of); + continue; + } + if (rv == -1) { + if (errno != EIO) { + if (!quiet) + putc('\n', stderr); + perror("non-IO error"); + return (EX_OSERR); + } + if (ioctl(fd, FD_GSTAT, &fdcs) == -1) + errx(EX_IOERR, + "floppy IO error, but no FDC status"); + nerrs++; + recoverable = fdcs.status[2] & + NE7_ST2_DD; + if (!quiet) { + printstatus(&fdcs); + fputs(" (", stderr); + if (!recoverable) + fputs("not ", stderr); + fputs("recoverable)", stderr); + } + if (!recover) { + if (!quiet) + putc('\n', stderr); + return (EX_IOERR); + } + memset(trackbuf, fillbyte, secsize); + if (recoverable) { + fdopts |= FDOPT_NOERROR; + if (ioctl(fd, FD_SOPTS, + &fdopts) == -1) + err(EX_OSERR, + "ioctl(fd, FD_SOPTS, FDOPT_NOERROR)"); + rv = read(fd, trackbuf, + secsize); + if (rv != secsize) + err(EX_IOERR, + "read() with FDOPT_NOERROR still fails"); + fdopts &= ~FDOPT_NOERROR; + (void)ioctl(fd, FD_SOPTS, + &fdopts); + } + if (!quiet) { + if (recoverable) + fprintf(stderr, + ": recovered"); + else + fprintf(stderr, + ": dummy"); + fprintf(stderr, + " data @ %#x ... %#x\n", + nbytes, + nbytes + secsize - 1); + } + nbytes += secsize; + fwrite(trackbuf, sizeof(unsigned char), + secsize, of); + fflush(of); + continue; + } + errx(EX_OSERR, "unexpected read() result: %d", + rv); + } + } + } + if (!quiet) { + putc('\n', stderr); + if (nerrs) + fprintf(stderr, "%d error%s\n", + nerrs, nerrs > 1? "s": ""); + } + + return (nerrs? EX_IOERR: EX_OK); +} + +void +printstatus(struct fdc_status *fdcsp) +{ + char msgbuf[100]; + + fprintf(stderr, + "\nFDC status ST0=%#x ST1=%#x ST2=%#x C=%u H=%u R=%u N=%u:\n", + fdcsp->status[0] & 0xff, + fdcsp->status[1] & 0xff, + fdcsp->status[2] & 0xff, + fdcsp->status[3] & 0xff, + fdcsp->status[4] & 0xff, + fdcsp->status[5] & 0xff, + fdcsp->status[6] & 0xff); + + if ((fdcsp->status[0] & NE7_ST0_IC_RC) != NE7_ST0_IC_AT) { + sprintf(msgbuf, "unexcpted interrupt code %#x", + fdcsp->status[0] & NE7_ST0_IC_RC); + } else { + strcpy(msgbuf, "unexpected error code in ST1/ST2"); + + if (fdcsp->status[1] & NE7_ST1_EN) + strcpy(msgbuf, "end of cylinder (wrong format)"); + else if (fdcsp->status[1] & NE7_ST1_DE) { + if (fdcsp->status[2] & NE7_ST2_DD) + strcpy(msgbuf, "CRC error in data field"); + else + strcpy(msgbuf, "CRC error in ID field"); + } else if (fdcsp->status[1] & NE7_ST1_MA) { + if (fdcsp->status[2] & NE7_ST2_MD) + strcpy(msgbuf, "no address mark in data field"); + else + strcpy(msgbuf, "no address mark in ID field"); + } else if (fdcsp->status[2] & NE7_ST2_WC) + strcpy(msgbuf, "wrong cylinder (format mismatch)"); + else if (fdcsp->status[1] & NE7_ST1_ND) + strcpy(msgbuf, "no data (sector not found)"); + } + fputs(msgbuf, stderr); +} |