xref: /minix/minix/commands/format/format.c (revision d0055759)
1 /*	format 1.1 - format PC floppy disk		Author: Kees J. Bot
2  *								5 Mar 1994
3  */
4 #define nil 0
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <fcntl.h>
11 #include <string.h>
12 #include <time.h>
13 #include <errno.h>
14 #include <limits.h>
15 #include <machine/diskparm.h>
16 #include <minix/minlib.h>
17 
18 /* Constants. */
19 #define SECTOR_SIZE	512
20 #define NR_HEADS	  2
21 #define MAX_SECTORS	 18	/* 1.44Mb is the largest. */
22 
23 /* Name error in <ibm/diskparm.h>, left over from the days floppies were
24  * single sided.
25  */
26 #define sectors_per_track	sectors_per_cylinder
27 
28 /* From floppy device number to drive/type/format-bit and back.  See fd(4). */
29 #define isfloppy(dev)		(((dev) & 0xFF00) == 0x0200)
30 #define fl_drive(dev)		(((dev) & 0x0003) >> 0)
31 #define fl_type(dev)		(((dev) & 0x007C) >> 2)
32 #define fl_format(dev)		(((dev) & 0x0080) >> 7)
33 #define fl_makedev(drive, type, fmt)	\
34 	((dev_t) (0x0200 | ((fmt) << 7) | ((type) << 2) | ((drive) << 0)))
35 
36 /* Recognize floppy types. */
37 #define NR_TYPES		7	/* # non-auto types */
38 #define isflauto(type)		((type) == 0)
39 #define isfltyped(type)		((unsigned) ((type) - 1) < NR_TYPES)
40 #define isflpart(type)		((unsigned) (type) >= 28)
41 
42 /* Formatting parameters per type.  (Most of these parameters have no use
43  * for formatting, disk_parameter_s probably matches a BIOS parameter table.)
44  */
45 typedef struct disk_parameter_s	fmt_params_t;
46 
47 typedef struct type_parameters {
48 	unsigned		media_size;
49 	unsigned		drive_size;
50 	fmt_params_t		fmt_params;
51 } type_parameters_t;
52 
53 #define DC	0	/* Don't care. */
54 
55 type_parameters_t parameters[NR_TYPES] = {
56 	/* mediasize       s1     off   sec/cyl  dlen       fill    start */
57 	/*       drivesize     s2   sizecode  gap    fmtgap     settle    */
58   /* pc */ {  360,  360, { DC, DC, DC, 2,  9, DC, DC, 0x50, 0xF6, DC, DC }},
59   /* at */ { 1200, 1200, { DC, DC, DC, 2, 15, DC, DC, 0x54, 0xF6, DC, DC }},
60   /* qd */ {  360,  720, { DC, DC, DC, 2,  9, DC, DC, 0x50, 0xF6, DC, DC }},
61   /* ps */ {  720,  720, { DC, DC, DC, 2,  9, DC, DC, 0x50, 0xF6, DC, DC }},
62   /* pat */{  360, 1200, { DC, DC, DC, 2,  9, DC, DC, 0x50, 0xF6, DC, DC }},
63   /* qh */ {  720, 1200, { DC, DC, DC, 2,  9, DC, DC, 0x50, 0xF6, DC, DC }},
64   /* PS */ { 1440, 1440, { DC, DC, DC, 2, 18, DC, DC, 0x54, 0xF6, DC, DC }},
65 };
66 
67 /* Per sector ID to be sent to the controller by the driver. */
68 typedef struct sector_id {
69 	unsigned char	cyl;
70 	unsigned char	head;
71 	unsigned char	sector;
72 	unsigned char	sector_size_code;
73 } sector_id_t;
74 
75 /* Data to be "written" to the driver to format a track.  (lseek to the track
76  * first.)  The first sector contains sector ID's, the second format params.
77  */
78 
79 typedef struct track_data {
80 	sector_id_t	sec_ids[SECTOR_SIZE / sizeof(sector_id_t)];
81 	fmt_params_t	fmt_params;
82 	char		padding[SECTOR_SIZE - sizeof(fmt_params_t)];
83 } track_data_t;
84 
report(const char * label)85 void report(const char *label)
86 {
87 	fprintf(stderr, "format: %s: %s\n", label, strerror(errno));
88 }
89 
fatal(const char * label)90 void fatal(const char *label)
91 {
92 	report(label);
93 	exit(1);
94 }
95 
format_track(int ffd,unsigned type,unsigned cyl,unsigned head)96 void format_track(int ffd, unsigned type, unsigned cyl, unsigned head)
97 /* Format a single track on a floppy. */
98 {
99 	type_parameters_t *tparams= &parameters[type - 1];
100 	track_data_t track_data;
101 	off_t track_pos;
102 	unsigned sector;
103 	unsigned nr_sectors= tparams->fmt_params.sectors_per_track;
104 	sector_id_t *sid;
105 
106 	memset(&track_data, 0, sizeof(track_data));
107 
108 	/* Set the sector id's.  (Note that sectors count from 1.) */
109 	for (sector= 0; sector <= nr_sectors; sector++) {
110 		sid= &track_data.sec_ids[sector];
111 
112 		sid->cyl= cyl;
113 		sid->head= head;
114 		sid->sector= sector + 1;
115 		sid->sector_size_code= tparams->fmt_params.sector_size_code;
116 	}
117 
118 	/* Format parameters. */
119 	track_data.fmt_params= tparams->fmt_params;
120 
121 	/* Seek to the right track. */
122 	track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE;
123 	if (lseek(ffd, track_pos, SEEK_SET) == -1) {
124 		fprintf(stderr,
125 		"format: seeking to cyl %u, head %u (pos %lld) failed: %s\n",
126 			cyl, head, track_pos, strerror(errno));
127 		exit(1);
128 	}
129 
130 	/* Format track. */
131 	if (write(ffd, &track_data, sizeof(track_data)) < 0) {
132 		fprintf(stderr,
133 			"format: formatting cyl %d, head %d failed: %s\n",
134 			cyl, head, strerror(errno));
135 		exit(1);
136 	}
137 
138 	/* Make sure the data is not just cached in a file system. */
139 	fsync(ffd);
140 }
141 
verify_track(int vfd,unsigned type,unsigned cyl,unsigned head)142 void verify_track(int vfd, unsigned type, unsigned cyl, unsigned head)
143 /* Verify a track by reading it.  On error read sector by sector. */
144 {
145 	type_parameters_t *tparams= &parameters[type - 1];
146 	off_t track_pos;
147 	unsigned sector;
148 	unsigned nr_sectors= tparams->fmt_params.sectors_per_track;
149 	size_t track_bytes;
150 	static char buf[MAX_SECTORS * SECTOR_SIZE];
151 	static unsigned bad_count;
152 
153 	/* Seek to the right track. */
154 	track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE;
155 	if (lseek(vfd, track_pos, SEEK_SET) == -1) {
156 		fprintf(stderr,
157 		"format: seeking to cyl %u, head %u (pos %lld) failed: %s\n",
158 			cyl, head, track_pos, strerror(errno));
159 		exit(1);
160 	}
161 
162 	/* Read the track whole. */
163 	track_bytes= nr_sectors * SECTOR_SIZE;
164 	if (read(vfd, buf, track_bytes) == track_bytes) return;
165 
166 	/* An error occurred, retry sector by sector. */
167 	for (sector= 0; sector < nr_sectors; sector++) {
168 		if (lseek(vfd, track_pos, SEEK_SET) == -1) {
169 			fprintf(stderr,
170 	"format: seeking to cyl %u, head %u, sector %u (pos %lld) failed: %s\n",
171 				cyl, head, sector, track_pos, strerror(errno));
172 			exit(1);
173 		}
174 
175 		switch (read(vfd, buf, SECTOR_SIZE)) {
176 		case -1:
177 			fprintf(stderr,
178 		"format: bad sector at cyl %u, head %u, sector %u (pos %lld)\n",
179 				cyl, head, sector, track_pos);
180 			bad_count++;
181 			break;
182 		case SECTOR_SIZE:
183 			/* Fine. */
184 			break;
185 		default:
186 			fprintf(stderr, "format: short read at pos %lld\n",
187 				track_pos);
188 			bad_count++;
189 		}
190 		track_pos+= SECTOR_SIZE;
191 		if (bad_count >= nr_sectors) {
192 	fprintf(stderr, "format: too many bad sectors, floppy unusable\n");
193 			exit(1);
194 		}
195 	}
196 }
197 
format_device(unsigned drive,unsigned type,int verify)198 void format_device(unsigned drive, unsigned type, int verify)
199 {
200 	int ffd, vfd;
201 	char *fmt_dev, *ver_dev;
202 	struct stat st;
203 	unsigned cyl, head;
204 	unsigned nr_cyls;
205 	type_parameters_t *tparams= &parameters[type - 1];
206 	int verbose= isatty(1);
207 
208 	fmt_dev= tmpnam(nil);
209 
210 	if (mknod(fmt_dev, S_IFBLK | 0700, fl_makedev(drive, type, 1)) < 0) {
211 		fprintf(stderr, "format: making format device failed: %s\n",
212 			strerror(errno));
213 		exit(1);
214 	}
215 
216 	if ((ffd= open(fmt_dev, O_WRONLY)) < 0 || fstat(ffd, &st) < 0) {
217 		report(fmt_dev);
218 		(void) unlink(fmt_dev);
219 		exit(1);
220 	}
221 
222 	(void) unlink(fmt_dev);
223 
224 	if (st.st_rdev != fl_makedev(drive, type, 1)) {
225 		/* Someone is trying to trick me. */
226 		exit(1);
227 	}
228 
229 	if (verify) {
230 		ver_dev= tmpnam(nil);
231 
232 		if (mknod(ver_dev, S_IFBLK | 0700, fl_makedev(drive, type, 0))
233 									< 0) {
234 			fprintf(stderr,
235 				"format: making verify device failed: %s\n",
236 				strerror(errno));
237 			exit(1);
238 		}
239 
240 		if ((vfd= open(ver_dev, O_RDONLY)) < 0) {
241 			report(ver_dev);
242 			(void) unlink(ver_dev);
243 			exit(1);
244 		}
245 
246 		(void) unlink(ver_dev);
247 	}
248 
249 	nr_cyls= tparams->media_size * (1024 / SECTOR_SIZE) / NR_HEADS
250 				/ tparams->fmt_params.sectors_per_track;
251 
252 	if (verbose) {
253 		printf("Formatting a %uk diskette in a %uk drive\n",
254 			tparams->media_size, tparams->drive_size);
255 	}
256 
257 	for (cyl= 0; cyl < nr_cyls; cyl++) {
258 		for (head= 0; head < NR_HEADS; head++) {
259 			if (verbose) {
260 				printf(" Cyl. %2u, Head %u\r", cyl, head);
261 				fflush(stdout);
262 			}
263 			/* After formatting a track we are too late to format
264 			 * the next track.  So we can sleep at most 1/6 sec to
265 			 * allow the above printf to get displayed before we
266 			 * lock Minix into the floppy driver again.
267 			 */
268 			if (verbose) usleep(50000);	/* 1/20 sec will do. */
269 			format_track(ffd, type, cyl, head);
270 			if (verify) verify_track(vfd, type, cyl, head);
271 		}
272 	}
273 	if (verbose) fputc('\n', stdout);
274 }
275 
usage(void)276 void usage(void)
277 {
278 	fprintf(stderr,
279 		"Usage: format [-v] <device> [<media size> [<drive size>]]\n");
280 	exit(1);
281 }
282 
main(int argc,char ** argv)283 int main(int argc, char **argv)
284 {
285 	char *device;
286 	unsigned drive;
287 	unsigned type;
288 	unsigned media_size;
289 	unsigned drive_size;
290 	int verify= 0;
291 	struct stat st0, st;
292 	char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1];
293 	char version[MNTNAMELEN], rw_flag[MNTFLAGLEN];
294 
295 	/* Option -v. */
296 	while (argc > 1 && argv[1][0] == '-') {
297 		char *p;
298 
299 		for (p= argv[1]; *p == '-' || *p == 'v'; p++) {
300 			if (*p == 'v') verify= 1;
301 		}
302 		if (*p != 0) usage();
303 		argc--;
304 		argv++;
305 		if (strcmp(argv[0], "--") == 0) break;
306 	}
307 
308 	if (argc < 2 || argc > 4) usage();
309 
310 	/* Check if the caller has read-write permission.  Use the access()
311 	 * call to check with the real uid & gid.  This program is usually
312 	 * set-uid root.
313 	 */
314 	device= argv[1];
315 	if (stat(device, &st0) < 0
316 		|| access(device, R_OK|W_OK) < 0
317 		|| stat(device, &st) < 0
318 		|| (errno= EACCES, 0)	/* set errno for following tests */
319 		|| st.st_dev != st0.st_dev
320 		|| st.st_ino != st0.st_ino
321 	) {
322 		fatal(device);
323 	}
324 
325 	if (!S_ISBLK(st.st_mode) || !isfloppy(st.st_rdev)) {
326 		fprintf(stderr, "format: %s: not a floppy device\n", device);
327 		exit(1);
328 	}
329 
330 	drive= fl_drive(st.st_rdev);
331 	type= fl_type(st.st_rdev);
332 
333 	/* The drive should not be mounted. */
334 	if (load_mtab("mkfs") < 0) exit(1);
335 
336 	while (get_mtab_entry(special, mounted_on, version, rw_flag) == 0) {
337 		if (stat(special, &st) >= 0 && isfloppy(st.st_rdev)
338 					&& fl_drive(st.st_rdev) == drive) {
339 			fprintf(stderr, "format: %s is mounted on %s\n",
340 				device, mounted_on);
341 			exit(1);
342 		}
343 	}
344 
345 	if (isflauto(type)) {
346 		/* Auto type 0 requires size(s). */
347 		unsigned long lmedia, ldrive;
348 		char *end;
349 
350 		if (argc < 3) {
351 			fprintf(stderr,
352 			"format: no size specified for auto floppy device %s\n",
353 				device);
354 			usage();
355 		}
356 
357 		lmedia= strtoul(argv[2], &end, 10);
358 		if (end == argv[2] || *end != 0 || lmedia > 20 * 1024)
359 			usage();
360 
361 		if (argc == 4) {
362 			ldrive= strtoul(argv[3], &end, 10);
363 			if (end == argv[3] || *end != 0 || ldrive > 20 * 1024)
364 				usage();
365 		} else {
366 			ldrive= lmedia;
367 		}
368 
369 		/* Silently correct wrong ordered sizes. */
370 		if (lmedia > ldrive) {
371 			media_size= ldrive;
372 			drive_size= lmedia;
373 		} else {
374 			media_size= lmedia;
375 			drive_size= ldrive;
376 		}
377 
378 		/* A 1.44M drive can do 720k diskettes with no extra tricks.
379 		 * Diddle with the 720k params so it is found.
380 		 */
381 		if (media_size == 720 && drive_size == 1440)
382 			parameters[4 - 1].drive_size= 1440;
383 
384 		/* Translate the auto type to a known type. */
385 		for (type= 1; type <= NR_TYPES; type++) {
386 			if (parameters[type - 1].media_size == media_size
387 				&& parameters[type - 1].drive_size == drive_size
388 			) break;
389 		}
390 
391 		if (!isfltyped(type)) {
392 			fprintf(stderr,
393 			"format: can't format a %uk floppy in a %uk drive\n",
394 				media_size, drive_size);
395 			exit(1);
396 		}
397 	} else
398 	if (isfltyped(type)) {
399 		/* No sizes needed for a non-auto type. */
400 
401 		if (argc > 2) {
402 			fprintf(stderr,
403 	"format: no sizes need to be specified for non-auto floppy device %s\n",
404 				device);
405 			usage();
406 		}
407 	} else
408 	if (isflpart(type)) {
409 		fprintf(stderr,
410 			"format: floppy partition %s can't be formatted\n",
411 			device);
412 		exit(1);
413 	} else {
414 		fprintf(stderr,
415 			"format: %s: can't format strange type %d\n",
416 			device, type);
417 	}
418 
419 	format_device(drive, type, verify);
420 	exit(0);
421 }
422