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