fdformat.c revision 134081
1218792Snp/*
2218792Snp * Copyright (C) 1992-1994,2001 by Joerg Wunsch, Dresden
3218792Snp * All rights reserved.
4218792Snp *
5218792Snp * Redistribution and use in source and binary forms, with or without
6218792Snp * modification, are permitted provided that the following conditions
7218792Snp * are met:
8218792Snp * 1. Redistributions of source code must retain the above copyright
9218792Snp *    notice, this list of conditions and the following disclaimer.
10218792Snp * 2. Redistributions in binary form must reproduce the above copyright
11218792Snp *    notice, this list of conditions and the following disclaimer in the
12218792Snp *    documentation and/or other materials provided with the distribution.
13218792Snp *
14218792Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15218792Snp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16218792Snp * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17218792Snp * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
18218792Snp * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19218792Snp * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20218792Snp * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21218792Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22218792Snp * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23218792Snp * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24218792Snp * POSSIBILITY OF SUCH DAMAGE.
25218792Snp *
26218792Snp * $FreeBSD: head/usr.sbin/fdformat/fdformat.c 134081 2004-08-20 15:14:25Z phk $
27218792Snp */
28218792Snp
29218792Snp#include <sys/types.h>
30218792Snp#include <sys/fdcio.h>
31218792Snp#include <sys/stat.h>
32218792Snp
33218792Snp#include <ctype.h>
34218792Snp#include <err.h>
35218792Snp#include <errno.h>
36218792Snp#include <fcntl.h>
37218792Snp#include <paths.h>
38218792Snp#include <stdio.h>
39218792Snp#include <stdlib.h>
40218792Snp#include <string.h>
41218792Snp#include <strings.h>
42218792Snp#include <sysexits.h>
43218792Snp#include <unistd.h>
44218792Snp
45218792Snp#include "fdutil.h"
46218792Snp
47218792Snpstatic void
48218792Snpformat_track(int fd, int cyl, int secs, int head, int rate,
49218792Snp	     int gaplen, int secsize, int fill, int interleave,
50218792Snp	     int offset)
51218792Snp{
52218792Snp	struct fd_formb f;
53218792Snp	int i, j, il[FD_MAX_NSEC + 1];
54218792Snp
55218792Snp	memset(il, 0, sizeof il);
56218792Snp	for(j = 0, i = 1 + offset; i <= secs + offset; i++) {
57218792Snp	    while(il[(j % secs) + 1])
58218792Snp		    j++;
59218792Snp	    il[(j % secs) + 1] = i;
60218792Snp	    j += interleave;
61218792Snp	}
62218792Snp
63218792Snp	f.format_version = FD_FORMAT_VERSION;
64218792Snp	f.head = head;
65218792Snp	f.cyl = cyl;
66218792Snp	f.transfer_rate = rate;
67218792Snp
68218792Snp	f.fd_formb_secshift = secsize;
69218792Snp	f.fd_formb_nsecs = secs;
70218792Snp	f.fd_formb_gaplen = gaplen;
71218792Snp	f.fd_formb_fillbyte = fill;
72218792Snp	for(i = 0; i < secs; i++) {
73218792Snp		f.fd_formb_cylno(i) = cyl;
74218792Snp		f.fd_formb_headno(i) = head;
75218792Snp		f.fd_formb_secno(i) = il[i+1];
76218792Snp		f.fd_formb_secsize(i) = secsize;
77218792Snp	}
78218792Snp	if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
79218792Snp		err(EX_OSERR, "ioctl(FD_FORM)");
80218792Snp}
81218792Snp
82218792Snpstatic int
83218792Snpverify_track(int fd, int track, int tracksize)
84218792Snp{
85218792Snp	static char *buf;
86218792Snp	static int bufsz;
87218792Snp	int fdopts = -1, ofdopts, rv = 0;
88218792Snp
89218792Snp	if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
90218792Snp		warn("warning: ioctl(FD_GOPTS)");
91218792Snp	else {
92218792Snp		ofdopts = fdopts;
93218792Snp		fdopts |= FDOPT_NORETRY;
94218792Snp		(void)ioctl(fd, FD_SOPTS, &fdopts);
95218792Snp	}
96218792Snp
97218792Snp	if (bufsz < tracksize)
98218792Snp		buf = realloc(buf, bufsz = tracksize);
99218792Snp	if (buf == 0)
100218792Snp		errx(EX_UNAVAILABLE, "out of memory");
101218792Snp	if (lseek (fd, (long) track * tracksize, 0) < 0)
102218792Snp		rv = -1;
103218792Snp	/* try twice reading it, without using the normal retrier */
104218792Snp	else if (read (fd, buf, tracksize) != tracksize
105218792Snp		 && read (fd, buf, tracksize) != tracksize)
106218792Snp		rv = -1;
107218792Snp	if (fdopts != -1)
108218792Snp		(void)ioctl(fd, FD_SOPTS, &ofdopts);
109218792Snp	return (rv);
110218792Snp}
111218792Snp
112218792Snpstatic void
113218792Snpusage (void)
114218792Snp{
115218792Snp	errx(EX_USAGE,
116218792Snp	     "usage: fdformat [-F fill] [-f fmt] [-s fmtstr] [-nqvy] device");
117218792Snp}
118218792Snp
119218792Snpstatic int
120218792Snpyes (void)
121218792Snp{
122218792Snp	char reply[256], *p;
123218792Snp
124218792Snp	reply[sizeof(reply) - 1] = 0;
125218792Snp	for (;;) {
126218792Snp		fflush(stdout);
127218792Snp		if (!fgets (reply, sizeof(reply) - 1, stdin))
128218792Snp			return (0);
129218792Snp		for (p=reply; *p==' ' || *p=='\t'; ++p)
130218792Snp			continue;
131218792Snp		if (*p=='y' || *p=='Y')
132218792Snp			return (1);
133218792Snp		if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
134218792Snp			return (0);
135218792Snp		printf("Answer `yes' or `no': ");
136218792Snp	}
137218792Snp}
138218792Snp
139218792Snpint
140218792Snpmain(int argc, char **argv)
141218792Snp{
142218792Snp	enum fd_drivetype type;
143218792Snp	struct fd_type fdt, newft, *fdtp;
144218792Snp	struct stat sb;
145218792Snp#define MAXPRINTERRS 10
146218792Snp	struct fdc_status fdcs[MAXPRINTERRS];
147218792Snp	int format, fill, quiet, verify, verify_only, confirm;
148218792Snp	int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
149218792Snp	int fdopts, flags;
150218792Snp	char *fmtstring, *device;
151218792Snp	const char *name, *descr;
152218792Snp
153218792Snp	format = quiet = verify_only = confirm = 0;
154218792Snp	verify = 1;
155218792Snp	fill = 0xf6;
156218792Snp	fmtstring = 0;
157218792Snp
158218792Snp	while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1)
159218792Snp		switch(c) {
160218792Snp		case 'F':	/* fill byte */
161218792Snp			if (getnum(optarg, &fill)) {
162218792Snp				fprintf(stderr,
163218792Snp			"Bad argument %s to -F option; must be numeric\n",
164218792Snp					optarg);
165218792Snp				usage();
166218792Snp			}
167218792Snp			break;
168218792Snp
169218792Snp		case 'f':	/* format in kilobytes */
170218792Snp			if (getnum(optarg, &format)) {
171218792Snp				fprintf(stderr,
172218792Snp			"Bad argument %s to -f option; must be numeric\n",
173218792Snp					optarg);
174218792Snp				usage();
175218792Snp			}
176218792Snp			break;
177218792Snp
178218792Snp		case 'n':	/* don't verify */
179218792Snp			verify = 0;
180218792Snp			break;
181218792Snp
182218792Snp		case 'q':	/* quiet */
183218792Snp			quiet = 1;
184218792Snp			break;
185218792Snp
186218792Snp		case 's':	/* format string with detailed options */
187218792Snp			fmtstring = optarg;
188218792Snp			break;
189218792Snp
190218792Snp		case 'v':	/* verify only */
191218792Snp			verify = 1;
192218792Snp			verify_only = 1;
193218792Snp			break;
194218792Snp
195218792Snp		case 'y':	/* confirm */
196218792Snp			confirm = 1;
197218792Snp			break;
198218792Snp
199218792Snp		default:
200218792Snp			usage();
201218792Snp		}
202218792Snp
203218792Snp	if(optind != argc - 1)
204218792Snp		usage();
205218792Snp
206218792Snp	if (stat(argv[optind], &sb) == -1 && errno == ENOENT) {
207218792Snp		/* try prepending _PATH_DEV */
208218792Snp		device = malloc(strlen(argv[optind]) + sizeof(_PATH_DEV) + 1);
209218792Snp		if (device == 0)
210218792Snp			errx(EX_UNAVAILABLE, "out of memory");
211218792Snp		strcpy(device, _PATH_DEV);
212218792Snp		strcat(device, argv[optind]);
213218792Snp		if (stat(device, &sb) == -1) {
214218792Snp			free(device);
215218792Snp			device = argv[optind]; /* let it fail below */
216218792Snp		}
217218792Snp	} else {
218218792Snp		device = argv[optind];
219218792Snp	}
220218792Snp
221218792Snp	if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
222218792Snp		err(EX_OSERR, "open(%s)", device);
223218792Snp
224218792Snp	/*
225218792Snp	 * Device initialization.
226218792Snp	 *
227218792Snp	 * First, get the device type descriptor.  This tells us about
228218792Snp	 * the media geometry data we need to format a medium.  It also
229218792Snp	 * lets us know quickly whether the device name actually points
230218792Snp	 * to a floppy disk drive.
231218792Snp	 *
232218792Snp	 * Then, obtain any drive options.  We're mainly interested to
233218792Snp	 * see whether we're currently working on a device with media
234218792Snp	 * density autoselection (FDOPT_AUTOSEL).  Then, we add the
235218792Snp	 * device option to tell the kernel not to log media errors,
236218792Snp	 * since we can handle them ourselves.  If the device does
237218792Snp	 * media density autoselection, we then need to set the device
238218792Snp	 * type appropriately, since by opening with O_NONBLOCK we
239218792Snp	 * told the driver to bypass media autoselection (otherwise we
240218792Snp	 * wouldn't stand a chance to format an unformatted or damaged
241218792Snp	 * medium).  We do not attempt to set the media type on any
242218792Snp	 * other devices since this is a privileged operation.  For the
243218792Snp	 * same reason, specifying -f and -s options is only possible
244218792Snp	 * for autoselecting devices.
245218792Snp	 *
246218792Snp	 * Finally, we are ready to turn off O_NONBLOCK, and start to
247218792Snp	 * actually format something.
248218792Snp	 */
249218792Snp	if(ioctl(fd, FD_GTYPE, &fdt) < 0)
250218792Snp		errx(EX_OSERR, "not a floppy disk: %s", device);
251218792Snp	if (ioctl(fd, FD_GDTYPE, &type) == -1)
252218792Snp		err(EX_OSERR, "ioctl(FD_GDTYPE)");
253218792Snp	if (ioctl(fd, FD_GOPTS, &fdopts) == -1)
254218792Snp		err(EX_OSERR, "ioctl(FD_GOPTS)");
255218792Snp	fdopts |= FDOPT_NOERRLOG;
256218792Snp	if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
257218792Snp		err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
258218792Snp	if (format) {
259218792Snp		getname(type, &name, &descr);
260218792Snp		fdtp = get_fmt(format, type);
261218792Snp		if (fdtp == 0)
262218792Snp			errx(EX_USAGE,
263218792Snp			    "unknown format %d KB for drive type %s",
264218792Snp			     format, name);
265218792Snp		fdt = *fdtp;
266218792Snp	}
267218792Snp	if (fmtstring) {
268218792Snp		parse_fmt(fmtstring, type, fdt, &newft);
269218792Snp		fdt = newft;
270218792Snp	}
271218792Snp	if (ioctl(fd, FD_STYPE, &fdt) < 0)
272218792Snp		err(EX_OSERR, "ioctl(FD_STYPE)");
273218792Snp	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
274218792Snp		err(EX_OSERR, "fcntl(F_GETFL)");
275218792Snp	flags &= ~O_NONBLOCK;
276218792Snp	if (fcntl(fd, F_SETFL, flags) == -1)
277218792Snp		err(EX_OSERR, "fcntl(F_SETFL)");
278218792Snp
279218792Snp	bytes_per_track = fdt.sectrac * (128 << fdt.secsize);
280218792Snp
281218792Snp	/* XXX  20/40 = 0.5 */
282218792Snp	tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40;
283218792Snp
284218792Snp	if (verify_only) {
285218792Snp		if(!quiet)
286218792Snp			printf("Verify %dK floppy `%s'.\n",
287218792Snp				fdt.tracks * fdt.heads * bytes_per_track / 1024,
288218792Snp				device);
289218792Snp	}
290218792Snp	else if(!quiet && !confirm) {
291218792Snp		printf("Format %dK floppy `%s'? (y/n): ",
292218792Snp			fdt.tracks * fdt.heads * bytes_per_track / 1024,
293218792Snp			device);
294218792Snp		if(!yes()) {
295218792Snp			printf("Not confirmed.\n");
296218792Snp			return (EX_UNAVAILABLE);
297218792Snp		}
298218792Snp	}
299218792Snp
300218792Snp	/*
301218792Snp	 * Formatting.
302218792Snp	 */
303218792Snp	if(!quiet) {
304218792Snp		printf("Processing ");
305218792Snp		for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++)
306218792Snp			putchar('-');
307218792Snp		printf("\rProcessing ");
308218792Snp		fflush(stdout);
309218792Snp	}
310218792Snp
311218792Snp	error = errs = 0;
312218792Snp
313218792Snp	for (track = 0; track < fdt.tracks * fdt.heads; track++) {
314218792Snp		if (!verify_only) {
315218792Snp			format_track(fd, track / fdt.heads, fdt.sectrac,
316218792Snp				track % fdt.heads, fdt.trans, fdt.f_gap,
317218792Snp				fdt.secsize, fill, fdt.f_inter,
318218792Snp				track % fdt.heads? fdt.offset_side2: 0);
319218792Snp			if(!quiet && !((track + 1) % tracks_per_dot)) {
320218792Snp				putchar('F');
321218792Snp				fflush(stdout);
322218792Snp			}
323218792Snp		}
324218792Snp		if (verify) {
325218792Snp			if (verify_track(fd, track, bytes_per_track) < 0) {
326218792Snp				error = 1;
327218792Snp				if (errs < MAXPRINTERRS && errno == EIO) {
328218792Snp					if (ioctl(fd, FD_GSTAT, fdcs + errs) ==
329218792Snp					    -1)
330218792Snp						errx(EX_IOERR,
331218792Snp					"floppy IO error, but no FDC status");
332218792Snp					errs++;
333218792Snp				}
334218792Snp			}
335218792Snp			if(!quiet && !((track + 1) % tracks_per_dot)) {
336218792Snp				if (!verify_only)
337218792Snp					putchar('\b');
338218792Snp				if (error) {
339218792Snp					putchar('E');
340218792Snp					error = 0;
341218792Snp				}
342218792Snp				else
343218792Snp					putchar('V');
344218792Snp				fflush(stdout);
345218792Snp			}
346218792Snp		}
347218792Snp	}
348218792Snp	if(!quiet)
349218792Snp		printf(" done.\n");
350218792Snp
351218792Snp	if (!quiet && errs) {
352218792Snp		fflush(stdout);
353218792Snp		fprintf(stderr, "Errors encountered:\nCyl Head Sect   Error\n");
354218792Snp		for (i = 0; i < errs && i < MAXPRINTERRS; i++) {
355218792Snp			fprintf(stderr, " %2d   %2d   %2d   ",
356218792Snp				fdcs[i].status[3], fdcs[i].status[4],
357218792Snp				fdcs[i].status[5]);
358218792Snp			printstatus(fdcs + i, 1);
359218792Snp			putc('\n', stderr);
360218792Snp		}
361218792Snp		if (errs >= MAXPRINTERRS)
362218792Snp			fprintf(stderr, "(Further errors not printed.)\n");
363218792Snp	}
364218792Snp
365218792Snp	return errs != 0;
366218792Snp}
367218792Snp