1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-14 10:09:48 +00:00

Initial import of fdread(1), a logical counterpart to fdwrite(1).

Its main purpose is to adapt automatically to the floppy parameters
(in particular the track size for efficient reading), and to allow a
simple error recovery for CRC-errored sectors.  Requires the newly
added fdc(4) options.
This commit is contained in:
Joerg Wunsch 2001-05-14 20:22:49 +00:00
parent 2995d1100c
commit b97809a770
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/JOERG/; revision=76589
3 changed files with 497 additions and 0 deletions

6
usr.sbin/fdread/Makefile Normal file
View File

@ -0,0 +1,6 @@
# $FreeBSD$
PROG= fdread
CFLAGS+=-Wall
.include <bsd.prog.mk>

184
usr.sbin/fdread/fdread.1 Normal file
View File

@ -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 .

307
usr.sbin/fdread/fdread.c Normal file
View File

@ -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);
}