257 lines
6.5 KiB
C
257 lines
6.5 KiB
C
/*
|
|
* fdformat.c - Low-level formats a floppy disk - Werner Almesberger
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <linux/fd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "c.h"
|
|
#include "blkdev.h"
|
|
#include "strutils.h"
|
|
#include "closestream.h"
|
|
#include "nls.h"
|
|
#include "xalloc.h"
|
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
static struct floppy_struct param;
|
|
|
|
|
|
static void format_begin(int ctrl)
|
|
{
|
|
if (ioctl(ctrl, FDFMTBEG, NULL) < 0)
|
|
err(EXIT_FAILURE, "ioctl: FDFMTBEG");
|
|
}
|
|
|
|
static void format_end(int ctrl)
|
|
{
|
|
if (ioctl(ctrl, FDFMTEND, NULL) < 0)
|
|
err(EXIT_FAILURE, "ioctl: FDFMTEND");
|
|
}
|
|
|
|
static void format_track_head(int ctrl, struct format_descr *descr)
|
|
{
|
|
if (ioctl(ctrl, FDFMTTRK, (long) descr) < 0)
|
|
err(EXIT_FAILURE, "ioctl: FDFMTTRK");
|
|
}
|
|
|
|
static void seek_track_head(int ctrl, struct format_descr *descr)
|
|
{
|
|
lseek(ctrl, ((off_t) descr->track * param.head + descr->head)
|
|
* param.sect * SECTOR_SIZE, SEEK_SET);
|
|
}
|
|
|
|
static void format_disk(int ctrl, unsigned int track_from, unsigned int track_to)
|
|
{
|
|
struct format_descr current;
|
|
|
|
printf(_("Formatting ... "));
|
|
fflush(stdout);
|
|
|
|
format_begin(ctrl);
|
|
|
|
for (current.track = track_from; current.track <= track_to; current.track++) {
|
|
for (current.head = 0; current.head < param.head; current.head++) {
|
|
printf("%3u/%u\b\b\b\b\b", current.track, current.head);
|
|
fflush(stdout);
|
|
format_track_head(ctrl, ¤t);
|
|
}
|
|
}
|
|
|
|
format_end(ctrl);
|
|
|
|
printf(" \b\b\b\b\b%s", _("done\n"));
|
|
}
|
|
|
|
static void verify_disk(int ctrl, unsigned int track_from, unsigned int track_to, unsigned int repair)
|
|
{
|
|
unsigned char *data;
|
|
struct format_descr current;
|
|
int track_size, count;
|
|
unsigned int retries_left;
|
|
|
|
track_size = param.sect * SECTOR_SIZE;
|
|
data = xmalloc(track_size);
|
|
printf(_("Verifying ... "));
|
|
fflush(stdout);
|
|
|
|
current.track = track_from;
|
|
current.head = 0;
|
|
seek_track_head (ctrl, ¤t);
|
|
|
|
for (current.track = track_from; current.track <= track_to; current.track++) {
|
|
for (current.head = 0; current.head < param.head; current.head++) {
|
|
int read_bytes;
|
|
|
|
printf("%3u\b\b\b", current.track);
|
|
fflush(stdout);
|
|
|
|
retries_left = repair;
|
|
do {
|
|
read_bytes = read(ctrl, data, track_size);
|
|
if (read_bytes != track_size) {
|
|
if (retries_left) {
|
|
format_begin(ctrl);
|
|
format_track_head(ctrl, ¤t);
|
|
format_end(ctrl);
|
|
seek_track_head (ctrl, ¤t);
|
|
retries_left--;
|
|
if (retries_left)
|
|
continue;
|
|
}
|
|
if (read_bytes < 0)
|
|
perror(_("Read: "));
|
|
fprintf(stderr,
|
|
_("Problem reading track/head %u/%u,"
|
|
" expected %d, read %d\n"),
|
|
current.track, current.head, track_size, read_bytes);
|
|
free(data);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
for (count = 0; count < track_size; count++)
|
|
if (data[count] != FD_FILL_BYTE) {
|
|
if (retries_left) {
|
|
format_begin(ctrl);
|
|
format_track_head(ctrl, ¤t);
|
|
format_end(ctrl);
|
|
seek_track_head (ctrl, ¤t);
|
|
retries_left--;
|
|
if (retries_left)
|
|
continue;
|
|
}
|
|
printf(_("bad data in track/head %u/%u\n"
|
|
"Continuing ... "), current.track, current.head);
|
|
fflush(stdout);
|
|
break;
|
|
}
|
|
break;
|
|
} while (retries_left);
|
|
}
|
|
}
|
|
|
|
free(data);
|
|
printf(_("done\n"));
|
|
}
|
|
|
|
static void __attribute__((__noreturn__)) usage(void)
|
|
{
|
|
FILE *out = stdout;
|
|
fputs(USAGE_HEADER, out);
|
|
fprintf(out, _(" %s [options] <device>\n"),
|
|
program_invocation_short_name);
|
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
fputs(_("Do a low-level formatting of a floppy disk.\n"), out);
|
|
|
|
fputs(USAGE_OPTIONS, out);
|
|
fputs(_(" -f, --from <N> start at the track N (default 0)\n"), out);
|
|
fputs(_(" -t, --to <N> stop at the track N\n"), out);
|
|
fputs(_(" -r, --repair <N> try to repair tracks failed during\n"
|
|
" the verification (max N retries)\n"), out);
|
|
fputs(_(" -n, --no-verify disable the verification after the format\n"), out);
|
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
printf(USAGE_HELP_OPTIONS(19));
|
|
printf(USAGE_MAN_TAIL("fdformat(8)"));
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ch;
|
|
int ctrl;
|
|
int verify = 1;
|
|
unsigned int repair = 0;
|
|
unsigned int track_from = 0;
|
|
unsigned int track_to = 0;
|
|
int has_user_defined_track_to = 0;
|
|
struct stat st;
|
|
|
|
static const struct option longopts[] = {
|
|
{"from", required_argument, NULL, 'f'},
|
|
{"to", required_argument, NULL, 't'},
|
|
{"repair", required_argument, NULL, 'r'},
|
|
{"no-verify", no_argument, NULL, 'n'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
close_stdout_atexit();
|
|
|
|
while ((ch = getopt_long(argc, argv, "f:t:r:nVh", longopts, NULL)) != -1)
|
|
switch (ch) {
|
|
case 'f':
|
|
track_from = strtou32_or_err(optarg, _("invalid argument - from"));
|
|
break;
|
|
case 't':
|
|
has_user_defined_track_to = 1;
|
|
track_to = strtou32_or_err(optarg, _("invalid argument - to"));
|
|
break;
|
|
case 'r':
|
|
repair = strtou32_or_err(optarg, _("invalid argument - repair"));
|
|
break;
|
|
case 'n':
|
|
verify = 0;
|
|
break;
|
|
case 'V':
|
|
print_version(EXIT_SUCCESS);
|
|
case 'h':
|
|
usage();
|
|
default:
|
|
errtryhelp(EXIT_FAILURE);
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
warnx(_("no device specified"));
|
|
errtryhelp(EXIT_FAILURE);
|
|
}
|
|
if (stat(argv[0], &st) < 0)
|
|
err(EXIT_FAILURE, _("stat of %s failed"), argv[0]);
|
|
if (!S_ISBLK(st.st_mode))
|
|
/* do not test major - perhaps this was an USB floppy */
|
|
errx(EXIT_FAILURE, _("%s: not a block device"), argv[0]);
|
|
ctrl = open_blkdev_or_file(&st, argv[0], O_RDWR);
|
|
if (ctrl < 0)
|
|
err(EXIT_FAILURE, _("cannot open %s"), argv[0]);
|
|
if (ioctl(ctrl, FDGETPRM, (long) ¶m) < 0)
|
|
err(EXIT_FAILURE, _("could not determine current format type"));
|
|
|
|
printf(_("%s-sided, %d tracks, %d sec/track. Total capacity %d kB.\n"),
|
|
(param.head == 2) ? _("Double") : _("Single"),
|
|
param.track, param.sect, param.size >> 1);
|
|
|
|
if (!has_user_defined_track_to)
|
|
track_to = param.track - 1;
|
|
|
|
if (track_from >= param.track)
|
|
err(EXIT_FAILURE, _("user defined start track exceeds the medium specific maximum"));
|
|
if (track_to >= param.track)
|
|
err(EXIT_FAILURE, _("user defined end track exceeds the medium specific maximum"));
|
|
if (track_from > track_to)
|
|
err(EXIT_FAILURE, _("user defined start track exceeds the user defined end track"));
|
|
|
|
format_disk(ctrl, track_from, track_to);
|
|
|
|
if (verify)
|
|
verify_disk(ctrl, track_from, track_to, repair);
|
|
|
|
if (close_fd(ctrl) != 0)
|
|
err(EXIT_FAILURE, _("close failed"));
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|