1/*
2 * Copyright (C) 1992-1994,2001 by Joerg Wunsch, Dresden
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/types.h>
30#include <sys/fdcio.h>
31#include <sys/stat.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <paths.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <strings.h>
42#include <sysexits.h>
43#include <unistd.h>
44
45#include "fdutil.h"
46
47static void
48format_track(int fd, int cyl, int secs, int head, int rate,
49	     int gaplen, int secsize, int fill, int interleave,
50	     int offset)
51{
52	struct fd_formb f;
53	int i, j, il[FD_MAX_NSEC + 1];
54
55	memset(il, 0, sizeof il);
56	for(j = 0, i = 1 + offset; i <= secs + offset; i++) {
57	    while(il[(j % secs) + 1])
58		    j++;
59	    il[(j % secs) + 1] = i;
60	    j += interleave;
61	}
62
63	f.format_version = FD_FORMAT_VERSION;
64	f.head = head;
65	f.cyl = cyl;
66	f.transfer_rate = rate;
67
68	f.fd_formb_secshift = secsize;
69	f.fd_formb_nsecs = secs;
70	f.fd_formb_gaplen = gaplen;
71	f.fd_formb_fillbyte = fill;
72	for(i = 0; i < secs; i++) {
73		f.fd_formb_cylno(i) = cyl;
74		f.fd_formb_headno(i) = head;
75		f.fd_formb_secno(i) = il[i+1];
76		f.fd_formb_secsize(i) = secsize;
77	}
78	(void)ioctl(fd, FD_FORM, (caddr_t)&f);
79}
80
81static int
82verify_track(int fd, int track, int tracksize)
83{
84	static char *buf;
85	static int bufsz;
86	int fdopts = -1, ofdopts, rv = 0;
87
88	if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
89		warn("warning: ioctl(FD_GOPTS)");
90	else {
91		ofdopts = fdopts;
92		fdopts |= FDOPT_NORETRY;
93		(void)ioctl(fd, FD_SOPTS, &fdopts);
94	}
95
96	if (bufsz < tracksize)
97		buf = realloc(buf, bufsz = tracksize);
98	if (buf == 0)
99		errx(EX_UNAVAILABLE, "out of memory");
100	if (lseek (fd, (long) track * tracksize, 0) < 0)
101		rv = -1;
102	/* try twice reading it, without using the normal retrier */
103	else if (read (fd, buf, tracksize) != tracksize
104		 && read (fd, buf, tracksize) != tracksize)
105		rv = -1;
106	if (fdopts != -1)
107		(void)ioctl(fd, FD_SOPTS, &ofdopts);
108	return (rv);
109}
110
111static void
112usage (void)
113{
114	errx(EX_USAGE,
115	     "usage: fdformat [-F fill] [-f fmt] [-s fmtstr] [-nqvy] device");
116}
117
118static int
119yes (void)
120{
121	char reply[256], *p;
122
123	reply[sizeof(reply) - 1] = 0;
124	for (;;) {
125		fflush(stdout);
126		if (!fgets (reply, sizeof(reply) - 1, stdin))
127			return (0);
128		for (p=reply; *p==' ' || *p=='\t'; ++p)
129			continue;
130		if (*p=='y' || *p=='Y')
131			return (1);
132		if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
133			return (0);
134		printf("Answer `yes' or `no': ");
135	}
136}
137
138int
139main(int argc, char **argv)
140{
141	enum fd_drivetype type;
142	struct fd_type fdt, newft, *fdtp;
143	struct stat sb;
144#define MAXPRINTERRS 10
145	struct fdc_status fdcs[MAXPRINTERRS];
146	int format, fill, quiet, verify, verify_only, confirm;
147	int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
148	int flags;
149	char *fmtstring, *device;
150	const char *name, *descr;
151
152	format = quiet = verify_only = confirm = 0;
153	verify = 1;
154	fill = 0xf6;
155	fmtstring = 0;
156
157	while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1)
158		switch(c) {
159		case 'F':	/* fill byte */
160			if (getnum(optarg, &fill)) {
161				fprintf(stderr,
162			"Bad argument %s to -F option; must be numeric\n",
163					optarg);
164				usage();
165			}
166			break;
167
168		case 'f':	/* format in kilobytes */
169			if (getnum(optarg, &format)) {
170				fprintf(stderr,
171			"Bad argument %s to -f option; must be numeric\n",
172					optarg);
173				usage();
174			}
175			break;
176
177		case 'n':	/* don't verify */
178			verify = 0;
179			break;
180
181		case 'q':	/* quiet */
182			quiet = 1;
183			break;
184
185		case 's':	/* format string with detailed options */
186			fmtstring = optarg;
187			break;
188
189		case 'v':	/* verify only */
190			verify = 1;
191			verify_only = 1;
192			break;
193
194		case 'y':	/* confirm */
195			confirm = 1;
196			break;
197
198		default:
199			usage();
200		}
201
202	if(optind != argc - 1)
203		usage();
204
205	if (stat(argv[optind], &sb) == -1 && errno == ENOENT) {
206		/* try prepending _PATH_DEV */
207		device = malloc(strlen(argv[optind]) + sizeof(_PATH_DEV) + 1);
208		if (device == 0)
209			errx(EX_UNAVAILABLE, "out of memory");
210		strcpy(device, _PATH_DEV);
211		strcat(device, argv[optind]);
212		if (stat(device, &sb) == -1) {
213			free(device);
214			device = argv[optind]; /* let it fail below */
215		}
216	} else {
217		device = argv[optind];
218	}
219
220	if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
221		err(EX_OSERR, "open(%s)", device);
222
223	/*
224	 * Device initialization.
225	 *
226	 * First, get the device type descriptor.  This tells us about
227	 * the media geometry data we need to format a medium.  It also
228	 * lets us know quickly whether the device name actually points
229	 * to a floppy disk drive.
230	 *
231	 * Then, obtain any drive options.  We're mainly interested to
232	 * see whether we're currently working on a device with media
233	 * density autoselection (FDOPT_AUTOSEL).  Then, we add the
234	 * device option to tell the kernel not to log media errors,
235	 * since we can handle them ourselves.  If the device does
236	 * media density autoselection, we then need to set the device
237	 * type appropriately, since by opening with O_NONBLOCK we
238	 * told the driver to bypass media autoselection (otherwise we
239	 * wouldn't stand a chance to format an unformatted or damaged
240	 * medium).  We do not attempt to set the media type on any
241	 * other devices since this is a privileged operation.  For the
242	 * same reason, specifying -f and -s options is only possible
243	 * for autoselecting devices.
244	 *
245	 * Finally, we are ready to turn off O_NONBLOCK, and start to
246	 * actually format something.
247	 */
248	if(ioctl(fd, FD_GTYPE, &fdt) < 0)
249		errx(EX_OSERR, "not a floppy disk: %s", device);
250	if (ioctl(fd, FD_GDTYPE, &type) == -1)
251		err(EX_OSERR, "ioctl(FD_GDTYPE)");
252	if (format) {
253		getname(type, &name, &descr);
254		fdtp = get_fmt(format, type);
255		if (fdtp == 0)
256			errx(EX_USAGE,
257			    "unknown format %d KB for drive type %s",
258			     format, name);
259		fdt = *fdtp;
260	}
261	if (fmtstring) {
262		parse_fmt(fmtstring, type, fdt, &newft);
263		fdt = newft;
264	}
265	if (ioctl(fd, FD_STYPE, &fdt) < 0)
266		err(EX_OSERR, "ioctl(FD_STYPE)");
267	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
268		err(EX_OSERR, "fcntl(F_GETFL)");
269	flags &= ~O_NONBLOCK;
270	if (fcntl(fd, F_SETFL, flags) == -1)
271		err(EX_OSERR, "fcntl(F_SETFL)");
272
273	bytes_per_track = fdt.sectrac * (128 << fdt.secsize);
274
275	/* XXX  20/40 = 0.5 */
276	tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40;
277
278	if (verify_only) {
279		if(!quiet)
280			printf("Verify %dK floppy `%s'.\n",
281				fdt.tracks * fdt.heads * bytes_per_track / 1024,
282				device);
283	}
284	else if(!quiet && !confirm) {
285		printf("Format %dK floppy `%s'? (y/n): ",
286			fdt.tracks * fdt.heads * bytes_per_track / 1024,
287			device);
288		if(!yes()) {
289			printf("Not confirmed.\n");
290			return (EX_UNAVAILABLE);
291		}
292	}
293
294	/*
295	 * Formatting.
296	 */
297	if(!quiet) {
298		printf("Processing ");
299		for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++)
300			putchar('-');
301		printf("\rProcessing ");
302		fflush(stdout);
303	}
304
305	error = errs = 0;
306
307	for (track = 0; track < fdt.tracks * fdt.heads; track++) {
308		if (!verify_only) {
309			format_track(fd, track / fdt.heads, fdt.sectrac,
310				track % fdt.heads, fdt.trans, fdt.f_gap,
311				fdt.secsize, fill, fdt.f_inter,
312				track % fdt.heads? fdt.offset_side2: 0);
313			if(!quiet && !((track + 1) % tracks_per_dot)) {
314				putchar('F');
315				fflush(stdout);
316			}
317		}
318		if (verify) {
319			if (verify_track(fd, track, bytes_per_track) < 0) {
320				error = 1;
321				if (errs < MAXPRINTERRS && errno == EIO) {
322					if (ioctl(fd, FD_GSTAT, fdcs + errs) ==
323					    -1)
324						errx(EX_IOERR,
325					"floppy IO error, but no FDC status");
326					errs++;
327				}
328			}
329			if(!quiet && !((track + 1) % tracks_per_dot)) {
330				if (!verify_only)
331					putchar('\b');
332				if (error) {
333					putchar('E');
334					error = 0;
335				}
336				else
337					putchar('V');
338				fflush(stdout);
339			}
340		}
341	}
342	if(!quiet)
343		printf(" done.\n");
344
345	if (!quiet && errs) {
346		fflush(stdout);
347		fprintf(stderr, "Errors encountered:\nCyl Head Sect   Error\n");
348		for (i = 0; i < errs && i < MAXPRINTERRS; i++) {
349			fprintf(stderr, " %2d   %2d   %2d   ",
350				fdcs[i].status[3], fdcs[i].status[4],
351				fdcs[i].status[5]);
352			printstatus(fdcs + i, 1);
353			putc('\n', stderr);
354		}
355		if (errs >= MAXPRINTERRS)
356			fprintf(stderr, "(Further errors not printed.)\n");
357	}
358
359	return errs != 0;
360}
361