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:
parent
2995d1100c
commit
b97809a770
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/JOERG/; revision=76589
6
usr.sbin/fdread/Makefile
Normal file
6
usr.sbin/fdread/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= fdread
|
||||
CFLAGS+=-Wall
|
||||
|
||||
.include <bsd.prog.mk>
|
184
usr.sbin/fdread/fdread.1
Normal file
184
usr.sbin/fdread/fdread.1
Normal 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
307
usr.sbin/fdread/fdread.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user