/*
* 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, (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(FILE * out)
{
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);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
fprintf(out, USAGE_MAN_TAIL("fdformat(8)"));
exit(out == stderr ? EXIT_FAILURE : 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);
atexit(close_stdout);
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':
printf(UTIL_LINUX_VERSION);
exit(EXIT_SUCCESS);
case 'h':
usage(stdout);
default:
errtryhelp(EXIT_FAILURE);
}
argc -= optind;
argv += optind;
if (argc < 1)
usage(stderr);
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;
}