xref: /illumos-gate/usr/src/cmd/fdformat/fdformat.c (revision 14b24e2b79293068c8e016a69ef1d872fb5e2fd5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * fdformat program - formats floppy disks, and then adds a label to them
28  *
29  *	 ****Warning, Warning, Warning, Warning*****
30  *	 This program runs suid root.  This change was made to
31  *	 allow it to umount a file system if it's mounted.
32  */
33 
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <memory.h>
40 #include <errno.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <volmgt.h>
44 #include <sys/isa_defs.h>
45 #include <sys/ioccom.h>
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <sys/file.h>
49 #include <sys/dklabel.h>
50 #include <sys/ioctl.h>
51 #include <sys/dkio.h>
52 #include <sys/fdio.h>
53 #include <sys/stat.h>
54 #include <sys/vtoc.h>
55 #include <sys/mnttab.h>
56 
57 /* DEFINES */
58 #if defined(_BIG_ENDIAN)
59 #define	getbyte(A, N)	(((unsigned char *)(&(A)))[N])
60 #define	htols(S)	((getbyte(S, 1) <<8) | getbyte(S, 0))
61 #elif defined(_LITTLE_ENDIAN)
62 #define	htols(S)	(*((ushort_t *)(&(S))))
63 #else
64 #error One of _BIG_ENDIAN or LITTLE_ENDIAN must be defined
65 #endif
66 
67 #define	getlobyte(A)	(A & 0xFF)
68 #define	gethibyte(A)	(A >> 8 & 0xFF)
69 #define	uppercase(c)	((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
70 #define	min(a, b)	((a) < (b) ? (a) : (b))
71 
72 /* FORMAT PATTERNS */
73 #define		PATTERN_1	0x55;
74 #define		PATTERN_2	0xaa;
75 #define		PATTERN_3	0xff;
76 #define		PATTERN_4	0x00;
77 
78 /* UNINITIALIZED DATA */
79 static 	struct fd_char 		fdchar;
80 static 	struct dk_geom 		fdgeom;
81 static 	struct dk_allmap 	allmap;
82 static 	struct dk_cinfo 	dkinfo;
83 
84 /* EXTERN */
85 extern char	*optarg;
86 extern int	optind;
87 
88 /* for verify buffers */
89 static uchar_t	*ibuf1;
90 static uchar_t	*obuf;
91 
92 static char	*myname;
93 
94 static 	int	fd_debug = 1;	/* 1 if debug XXX */
95 static 	int	b_flag = 0;	/* install a volume label to the diskette */
96 static 	int	d_flag = 0;	/* format the diskette in dos format */
97 static 	int	D_flag = 0;	/* double (aka low) density flag */
98 static 	int	e_flag = 0;	/* "eject" diskette when done (if supported) */
99 static 	int	E_flag = 0;	/* extended density */
100 static 	int	f_flag = 0;	/* "force" (no confirmation before start) */
101 static 	int	H_flag = 0;	/* high density */
102 static 	int	m_flag = 0;	/* medium density */
103 static 	int	n_flag = 0;	/* format the diskette in NEC-DOS format */
104 static 	int	q_flag = 0;	/* quiet format flag */
105 static 	int	U_flag = 0;	/* automatically unmount if it's mounted */
106 static 	int	v_flag = 0;	/* verify format/diskette flag */
107 static 	int	x_flag = 0;	/* skip the format, only install SunOS label */
108 				/* or DOS file system */
109 static 	int	z_flag = 0;	/* debugging only, setting partial formatting */
110 static 	int	interleave = 1;	/* interleave factor */
111 
112 static	uid_t	euid = 0;	/* stores effective user id */
113 
114 struct bios_param_blk {
115 	uchar_t	b_bps[2];		/* bytes per sector */
116 	uchar_t	b_spcl;			/* sectors per alloction unit */
117 	uchar_t	b_res_sec[2];		/* reserved sectors, starting at 0 */
118 	uchar_t	b_nfat;			/* number of FATs */
119 	uchar_t	b_rdirents[2];		/* number of root directory entries */
120 	uchar_t	b_totalsec[2];		/* total sectors in logical image */
121 	char	b_mediadescriptor;	/* media descriptor byte */
122 	uchar_t	b_fatsec[2];		/* number of sectors per FAT */
123 	uchar_t	b_spt[2];		/* sectors per track */
124 	uchar_t	b_nhead[2];		/* number of heads */
125 	uchar_t	b_hiddensec[2];		/* number of hidden sectors */
126 };
127 
128 /*
129  * ON-private functions from libvolmgt
130  */
131 char	*_media_oldaliases(char *name);
132 int	_dev_mounted(char *path);
133 int	_dev_unmount(char *path);
134 
135 /*
136  * local functions
137  */
138 static void	usage(char *);
139 static int	verify(int, int, int);
140 static void	write_SunOS_label(int, char *, struct vtoc *);
141 static int	valid_DOS_boot(char *, uchar_t **);
142 static void	write_DOS_label(int, uchar_t *, int, char *, char *,
143 			struct  bios_param_blk *, int);
144 static void	write_NEC_DOS_label(int, char *);
145 static int	check_mount();
146 static void	format_diskette(int, char *, struct vtoc *,
147 				struct  bios_param_blk *,  int *);
148 static void	restore_default_chars(int fd,
149 				    struct fd_char save_fdchar,
150 				    struct dk_allmap save_allmap);
151 
152 int
153 main(int argc, char **argv)
154 {
155 	int	altsize = 0;
156 	int	fd;
157 	int	i;
158 	uchar_t	*altboot = NULL;
159 	char	*altbootname = NULL;
160 	char	*dev_name = NULL, *real_name, *alias_name;
161 	char	*vollabel = "";
162 	struct  vtoc fd_vtoc;
163 	struct	bios_param_blk bpb;
164 	int	rdirsec;
165 	char    *nullstring = "";
166 
167 	(void) setlocale(LC_ALL, "");
168 
169 #if !defined(TEXT_DOMAIN)
170 #define	TEXT_DOMAIN	"SYS_TEST"
171 #endif
172 
173 	(void) textdomain(TEXT_DOMAIN);
174 
175 	myname = argv[0];
176 	while ((i = getopt(argc, argv, "B:b:dDeEfhHlLmMxqt:UvVZ?")) != -1) {
177 		switch (i) {
178 
179 		case 'B':
180 			altbootname = strdup(optarg);
181 			d_flag++;
182 			/* check for valid boot file now */
183 			altsize = valid_DOS_boot(altbootname, &altboot);
184 			if (!altsize) {
185 				(void) fprintf(stderr, gettext(
186 				    "%s: invalid boot loader\n"), myname);
187 				exit(1);
188 			}
189 			break;
190 
191 		case 'b':
192 			b_flag++;
193 			vollabel = strdup(optarg);
194 			break;
195 
196 		case 'd':
197 			/* format a MS-DOS diskette */
198 			d_flag++;
199 			break;
200 
201 		case 'D':
202 		case 'L':
203 		case 'l':
204 			/* format a Double density 720KB (or 360KB) disk */
205 			D_flag++;
206 			break;
207 
208 		case 'e':
209 			/* eject diskette when done */
210 			e_flag++;
211 			break;
212 
213 		case 'E':
214 			/* format an 2.88MB Extended density disk */
215 			E_flag++;
216 			break;
217 
218 		case 'f':
219 			/* don't ask for confirmation */
220 			f_flag++;
221 			break;
222 
223 		case 'H':
224 		case 'h':
225 			/* format a High density 1.2MB or 1.44MB disk */
226 			H_flag++;
227 			break;
228 
229 #if 0
230 		case 'i':
231 			/* interleave factor */
232 			interleave = atol(optarg);
233 			if (interleave <= 0) {
234 				(void) fprintf(stderr, gettext(
235 				    "%s: invalid interleave\n"), myname);
236 				exit(1);
237 			}
238 			break;
239 #endif
240 
241 		case 'M':
242 		case 'm':
243 			/* format a 3.5" HD disk to 1.2MB */
244 			m_flag++;
245 			break;
246 
247 		case 'x':
248 			/* skip format, just write label */
249 			x_flag++;
250 			break;
251 
252 		case 'q':
253 			/* quiet format */
254 			q_flag++;
255 			break;
256 
257 		case 't':
258 			/* Type of DOS formatting: NEC or MS */
259 			if (strcmp(optarg, "nec") == 0) {
260 				n_flag++;
261 			}
262 			if (strcmp(optarg, "dos") == 0) {
263 				d_flag++;
264 			}
265 			break;
266 
267 		case 'U':
268 			/* umount filesystem if mounted */
269 			U_flag++;
270 			break;
271 
272 		case 'v':
273 		case 'V':
274 			/* verify the diskette after format */
275 			v_flag++;
276 			break;
277 
278 		case 'Z':
279 			/* for debug only, format cyl 0 only */
280 			if (!fd_debug) {
281 				usage(gettext("unknown argument"));
282 				/* NOTREACHED */
283 			}
284 			(void) printf(gettext("\nFormat cyl Zero only\n"));
285 			z_flag++;
286 			break;
287 
288 		default:
289 			usage(" ");
290 			/* NOTREACHED */
291 		}
292 	}
293 
294 	if (optind < argc -1) {
295 		usage(gettext("more than one device name argument"));
296 		/* NOTREACHED */
297 	}
298 	if (optind == argc -1) {
299 		dev_name = argv[optind];
300 	}
301 	if (D_flag && H_flag) {
302 		usage(gettext("switches -D, -L and -H incompatible"));
303 		/* NOTREACHED */
304 	}
305 	if (D_flag && E_flag) {
306 		usage(gettext("switches -D, -L and -E incompatible"));
307 		/* NOTREACHED */
308 	}
309 	if (H_flag && E_flag) {
310 		usage(gettext("switches -H and -E incompatible"));
311 		/* NOTREACHED */
312 	}
313 	if (n_flag && d_flag) {
314 		usage(gettext("switches nec and dos incompatible"));
315 		/* NOTREACHED */
316 	}
317 	if (n_flag && !m_flag) {
318 		usage(gettext("switch -M required for NEC-DOS"));
319 		/* NOTREACHED */
320 	}
321 	if (D_flag && m_flag) {
322 		usage(gettext("switches -D, -L and -M incompatible"));
323 		/* NOTREACHED */
324 	}
325 	if (d_flag && m_flag) {
326 		usage(gettext("switches -d and -M incompatible"));
327 		/* NOTREACHED */
328 	}
329 
330 	if (dev_name == NULL)
331 		dev_name = "floppy";
332 
333 	if ((real_name = media_findname(dev_name)) == NULL) {
334 		if ((alias_name = _media_oldaliases(dev_name)) != NULL)
335 			real_name = media_findname(alias_name);
336 		if (real_name == NULL) {
337 			(void) fprintf(stderr,
338 gettext("No such volume (or no media in specified device): %s\n"),
339 					dev_name);
340 			exit(1);
341 		}
342 	}
343 
344 	/*
345 	 * This check is required because program runs suid root.
346 	 */
347 	if (access(real_name, R_OK|W_OK) < 0) {
348 		perror(real_name);
349 		exit(1);
350 	}
351 
352 	/* store callers euid */
353 
354 	euid = geteuid();
355 
356 	/*
357 	 * See if the given device name is mounted.  If this check isn't done
358 	 * before the open, the open will fail.  The failed open will not
359 	 * indicate that the device is mounted, only that it's busy
360 	 */
361 	if (_dev_mounted(real_name)) {
362 		if (U_flag) {
363 			if (!_dev_unmount(real_name)) {
364 				(void) fprintf(stderr,
365 					gettext("%s: umount of %s failed\n"),
366 				myname, real_name);
367 				exit(1);
368 			}
369 		} else {
370 			(void) fprintf(stderr,
371 				gettext("%s: %s is mounted (use -U flag)\n"),
372 				myname, real_name);
373 			exit(1);
374 		}
375 	}
376 
377 	/* Set to user access permissions to open file */
378 	(void) seteuid(getuid());
379 
380 	if ((fd = open(real_name, O_NDELAY | O_RDWR | O_EXCL)) == -1) {
381 		if (errno == EROFS) {
382 			(void) fprintf(stderr,
383 			    gettext("%s: \"%s\" is write protected\n"),
384 			    myname, real_name);
385 			exit(1);
386 		}
387 		/* XXX ought to check for "drive not installed", etc. */
388 		(void) fprintf(stderr, gettext("%s: could not open \"%s\": "),
389 		    myname, real_name);
390 		perror(nullstring);
391 		exit(1);
392 	}
393 
394 	/* restore effective id */
395 	(void) seteuid(euid);
396 
397 	if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
398 		(void) fprintf(stderr,
399 			gettext("%s: DKIOCINFO failed, "), myname);
400 		perror(nullstring);
401 		exit(3);
402 	}
403 
404 	/* See if there are any mounted partitions. */
405 	if (check_mount() != 0) {
406 			exit(3);
407 	}
408 
409 	/*
410 	 * The fd_vtoc, bpb, and rdirsec structures will be
411 	 * partially filled in by format_diskette().
412 	 * This was done so that write_DOS_label(),
413 	 * write_SunOS_label(), and write_NEC_DOS_label() could be
414 	 * device independent.  If a new device needs to be added to
415 	 * fdformat, a new format function like format_diskette should
416 	 * be added.  This function should fill in fd_vtoc, bpb, and
417 	 * rdirsec with device dependent information.
418 	 */
419 	(void) memset((void *)&fd_vtoc, (char)0, sizeof (struct vtoc));
420 	(void) memset((void *)&bpb, (char)0, sizeof (struct  bios_param_blk));
421 
422 	format_diskette(fd, real_name, &fd_vtoc, &bpb, &rdirsec);
423 
424 	if (d_flag)
425 		write_DOS_label(fd, altboot, altsize, altbootname,
426 				vollabel, &bpb, rdirsec);
427 	else if (n_flag)
428 		write_NEC_DOS_label(fd, vollabel);
429 	else
430 		write_SunOS_label(fd, vollabel, &fd_vtoc);
431 
432 	if (e_flag)
433 		/* eject media if possible */
434 		if (ioctl(fd, FDEJECT, 0)) {
435 			(void) fprintf(stderr,
436 			    gettext("%s: could not eject diskette, "), myname);
437 			perror(nullstring);
438 			exit(3);
439 		}
440 
441 	return (0);
442 }
443 
444 /*
445  * Inputs: file descriptor for the device and the device name.
446  * Oututs: the fd_vtoc will be partially filled in with the
447  *         device specific information such as partition
448  *         information and ascillabel. bpb and rdirsec will
449  *	   also be partially filled in with device specific information
450  */
451 void
452 format_diskette(int fd, char *real_name, struct vtoc *fd_vtoc,
453 				struct  bios_param_blk *bpb, int *rdirsec)
454 {
455 	int	transfer_rate = 1000;   /* transfer rate code */
456 	int	sec_size = 512;		/* sector size */
457 	uchar_t	gap = 0x54;		/* format gap size */
458 	uchar_t *fbuf, *p;
459 	char    *capacity = NULL;
460 	int	cyl_size;
461 	int	i;
462 	int	chgd;			/* for testing disk changed/present */
463 	int	cyl, hd;
464 	int	size_of_part, size_of_dev;
465 	int	spt = 36;		/* sectors per track */
466 	int	drive_size;
467 	uchar_t	num_cyl = 80;		/*  max number of cylinders */
468 	char    *nullstring = "";
469 	struct fd_char save_fdchar;	/* original diskette characteristics */
470 	struct dk_allmap save_allmap;	/* original diskette partition info */
471 
472 	/* FDRAW ioctl command structures for seeking and formatting */
473 	struct fd_raw fdr_seek = {
474 		FDRAW_SEEK, 0, 0, 0, 0, 0, 0, 0, 0, 0,
475 		3,
476 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
477 		0,
478 		0
479 	};
480 
481 	struct fd_raw fdr_form = {
482 		0x4D, 0, 2, 0, 0x54, (char)0xA5, 0, 0, 0, 0,
483 		6,
484 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
485 		0,	/* nbytes */
486 		0	/* addr */
487 	};
488 
489 
490 	/*
491 	 * restore drive to default geometry and characteristics
492 	 * (probably not implemented on sparc)
493 	 */
494 	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
495 
496 	/* get the default partititon maps */
497 	if (ioctl(fd, DKIOCGAPART, &allmap) == -1) {
498 		(void) fprintf(stderr,
499 		    gettext("%s: DKIOCGAPART failed, "), myname);
500 		perror(nullstring);
501 		exit(3);
502 	}
503 
504 	/* Save the original default partition maps */
505 	save_allmap = allmap;
506 
507 	/* find out the characteristics of the default diskette */
508 	if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) {
509 		(void) fprintf(stderr,
510 		    gettext("%s: FDIOGCHAR failed, "), myname);
511 		perror(nullstring);
512 		exit(3);
513 	}
514 
515 	/* Save the original characteristics of the default diskette */
516 	save_fdchar = fdchar;
517 
518 	/*
519 	 * The user may only format the entire diskette.
520 	 * formatting partion a or b is not allowed
521 	 */
522 	size_of_part = allmap.dka_map[dkinfo.dki_partition].dkl_nblk
523 			* DEV_BSIZE;
524 	size_of_dev = fdchar.fdc_ncyl * fdchar.fdc_nhead
525 			* fdchar.fdc_secptrack * fdchar.fdc_sec_size;
526 
527 	if (size_of_part != size_of_dev) {
528 		(void) fprintf(stderr,
529 			/*CSTYLED*/
530 			gettext("%s: The entire diskette must be formatted. Invalid device name.\n"),
531 			myname);
532 		exit(3);
533 	}
534 
535 
536 	/* find out the geometry of the drive */
537 	if (ioctl(fd, DKIOCGGEOM, &fdgeom) == -1) {
538 		(void) fprintf(stderr,
539 		    gettext("%s: DKIOCGGEOM failed, "), myname);
540 		perror(nullstring);
541 		exit(3);
542 	}
543 
544 #ifdef sparc
545 	fdchar.fdc_medium = 3;
546 #endif
547 	if (fdchar.fdc_medium == 5)
548 		drive_size = 5;
549 	else
550 		drive_size = 3;
551 
552 	/*
553 	 * set proper density flag in case we're formating to default
554 	 * characteristics because no density switch was input
555 	 */
556 	if ((E_flag | H_flag | D_flag | m_flag) == 0) {
557 		switch (fdchar.fdc_transfer_rate) {
558 		case 1000:
559 			/* assumes only ED uses 1.0 MB/sec */
560 			E_flag++;
561 			break;
562 		case 500:
563 		default:
564 			/*
565 			 * default to HD even though High density and
566 			 * "medium" density both use 500 KB/sec
567 			 */
568 			H_flag++;
569 			break;
570 #ifndef sparc
571 		case 250:
572 			/* assumes only DD uses 250 KB/sec */
573 			D_flag++;
574 			break;
575 #endif
576 		}
577 	}
578 
579 	if (H_flag) {
580 		transfer_rate = 500;
581 		num_cyl = 80;
582 		sec_size = 512;
583 		if (drive_size == 5) {
584 			(void) strcpy(fd_vtoc->v_asciilabel,
585 				"5.25\" floppy cyl 80 alt 0 hd 2 sec 15");
586 			spt = 15;
587 			capacity = "1.2 MB";
588 		} else {
589 			(void) strcpy(fd_vtoc->v_asciilabel,
590 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 18");
591 			spt = 18;
592 			capacity = "1.44 MB";
593 		}
594 		gap = 0x54;
595 	} else if (D_flag) {
596 		transfer_rate = 250;
597 		if (drive_size == 5) {
598 			(void) strcpy(fd_vtoc->v_asciilabel,
599 				"5.25\" floppy cyl 40 alt 0 hd 2 sec 9");
600 			if (fdchar.fdc_transfer_rate == 500) {
601 				/*
602 				 * formatting a 360KB DD diskette in
603 				 * a 1.2MB drive is not a good idea
604 				 */
605 				transfer_rate = 300;
606 				fdchar.fdc_steps = 2;
607 			}
608 			num_cyl = 40;
609 			gap = 0x50;
610 			capacity = "360 KB";
611 		} else {
612 			(void) strcpy(fd_vtoc->v_asciilabel,
613 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 9");
614 			num_cyl = 80;
615 			gap = 0x54;
616 			capacity = "720 KB";
617 		}
618 		sec_size = 512;
619 		spt = 9;
620 	} else if (m_flag) {
621 #ifdef sparc
622 		transfer_rate = 500;
623 #else
624 		/*
625 		 * 416.67 KB/sec is the effective transfer rate of a "medium"
626 		 * density diskette spun at 300 rpm instead of 360 rpm
627 		 */
628 		transfer_rate = 417;
629 #endif
630 		(void) strcpy(fd_vtoc->v_asciilabel,
631 				"3.5\" floppy cyl 77 alt 0 hd 2 sec 8");
632 		num_cyl = 77;
633 		sec_size = 1024;
634 		spt = 8;
635 		gap = 0x74;
636 		capacity = "1.2 MB";
637 	} else if (E_flag) {
638 		(void) strcpy(fd_vtoc->v_asciilabel,
639 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 36");
640 		transfer_rate = 1000;
641 		num_cyl = 80;
642 		sec_size = 512;
643 		spt = 36;
644 		gap = 0x54;
645 		capacity = "2.88 MB";
646 	}
647 	/*
648 	 * Medium density diskettes have 1024 byte blocks.  The dk_map
649 	 * structure in dklabel.h assumes the blocks size is DEVBSIZE (512)
650 	 * bytes.  The dkl_nblk field is in terms of DEVBSIZE byte blocks
651 	 * while the spt variable is in terms of the true block size on
652 	 * the diskette.
653 	 */
654 	if (allmap.dka_map[2].dkl_nblk !=
655 			(2 * num_cyl * spt * (m_flag ? 2 : 1))) {
656 		allmap.dka_map[1].dkl_cylno = num_cyl - 1;
657 		allmap.dka_map[0].dkl_nblk = 2 * (num_cyl - 1) * spt *
658 							(m_flag ? 2 : 1);
659 		allmap.dka_map[1].dkl_nblk = 2 * spt * (m_flag ? 2 : 1);
660 		allmap.dka_map[2].dkl_nblk = 2 * num_cyl * spt *
661 							(m_flag ? 2 : 1);
662 		if (allmap.dka_map[3].dkl_nblk)
663 			allmap.dka_map[3].dkl_nblk = 2 * (num_cyl - 1) * spt *
664 							(m_flag ? 2 : 1);
665 		if (allmap.dka_map[4].dkl_nblk)
666 			allmap.dka_map[4].dkl_nblk =
667 					2 * spt * (m_flag ? 2 : 1);
668 	}
669 
670 
671 	/* initialize the vtoc structure */
672 	fd_vtoc->v_nparts = 3;
673 
674 	fd_vtoc->v_part[0].p_start = 0;
675 	fd_vtoc->v_part[0].p_size = ((num_cyl - 1) * 2 * spt *
676 							(m_flag ? 2 : 1));
677 	fd_vtoc->v_part[1].p_start = ((num_cyl - 1) * 2 * spt *
678 							(m_flag ? 2 : 1));
679 	fd_vtoc->v_part[1].p_size = 2 * spt * (m_flag ? 2 : 1);
680 
681 	fd_vtoc->v_part[2].p_start = 0;
682 	fd_vtoc->v_part[2].p_size = num_cyl * 2 * spt * (m_flag ? 2 : 1);
683 
684 	/* initialize the bios parameter blockstructure */
685 	bpb->b_nfat = 2;
686 	if (E_flag && drive_size == 3) {
687 		bpb->b_spcl = 2;
688 		*rdirsec = (ushort_t)240;
689 		bpb->b_mediadescriptor = (char)0xF0;
690 		bpb->b_fatsec[0] = 9;
691 		bpb->b_fatsec[1] = 0;
692 	} else if (H_flag) {
693 		if (drive_size == 5) {
694 			bpb->b_spcl = 1;
695 			*rdirsec = 224;
696 			bpb->b_mediadescriptor = (char)0xF9;
697 			bpb->b_fatsec[0] = 7;
698 			bpb->b_fatsec[1] = 0;
699 		} else {
700 			bpb->b_spcl = 1;
701 			*rdirsec = 224;
702 			bpb->b_mediadescriptor = (char)0xF0;
703 			bpb->b_fatsec[0] = 9;
704 			bpb->b_fatsec[1] = 0;
705 		}
706 	} else if (drive_size == 5) {
707 		bpb->b_spcl = 2;
708 		*rdirsec = 112;
709 		bpb->b_mediadescriptor = (char)0xFD;
710 		bpb->b_fatsec[0] = 2;
711 		bpb->b_fatsec[1] = 0;
712 	} else if (drive_size == 3) {
713 		bpb->b_spcl = 2;
714 		*rdirsec = 112;
715 		bpb->b_mediadescriptor = (char)0xF9;
716 		bpb->b_fatsec[0] = 3;
717 		bpb->b_fatsec[1] = 0;
718 	}
719 
720 
721 
722 #ifndef sparc
723 	if (num_cyl > fdchar.fdc_ncyl || spt > fdchar.fdc_secptrack ||
724 	    transfer_rate > fdchar.fdc_transfer_rate) {
725 		(void) fprintf(stderr,
726 		    gettext("%s: drive not capable of requested density, "),
727 		    myname);
728 		perror(nullstring);
729 		exit(3);
730 	}
731 #endif
732 	if (num_cyl != fdchar.fdc_ncyl || spt != fdchar.fdc_secptrack ||
733 	    transfer_rate != fdchar.fdc_transfer_rate) {
734 		/*
735 		 * -- CAUTION --
736 		 * The SPARC fd driver is using a non-zero value in
737 		 * fdc_medium to indicate the 360 rpm, 77 track,
738 		 * 9 sectors/track, 1024 bytes/sector mode of operation
739 		 * (similar to an 8", DS/DD, 1.2 MB floppy).
740 		 *
741 		 * The x86 fd driver uses fdc_medium as the diameter
742 		 * indicator, either 3 or 5.  It should not be modified.
743 		 */
744 #ifdef sparc
745 		fdchar.fdc_medium = m_flag ? 1 : 0;
746 #endif
747 		fdchar.fdc_transfer_rate = transfer_rate;
748 		fdchar.fdc_ncyl = num_cyl;
749 		fdchar.fdc_sec_size = sec_size;
750 		fdchar.fdc_secptrack = spt;
751 
752 		if (ioctl(fd, FDIOSCHAR, &fdchar) == -1) {
753 			(void) fprintf(stderr, gettext(
754 			    "%s: FDIOSCHAR (density selection) failed, "),
755 			    myname);
756 
757 			/* restore the default characteristics */
758 			restore_default_chars(fd, save_fdchar, save_allmap);
759 			perror(nullstring);
760 			exit(3);
761 		}
762 		if (ioctl(fd, DKIOCSAPART, &allmap) == -1) {
763 			(void) fprintf(stderr,
764 			    gettext("%s: DKIOCSAPART failed, "),
765 			    myname);
766 
767 			/* restore the default characteristics */
768 			restore_default_chars(fd, save_fdchar, save_allmap);
769 
770 			perror(nullstring);
771 			exit(3);
772 		}
773 	}
774 
775 	if (interleave != 1 && interleave != fdgeom.dkg_intrlv) {
776 		fdgeom.dkg_intrlv = interleave;
777 		if (ioctl(fd, DKIOCSGEOM, &fdgeom) == -1) {
778 			(void) fprintf(stderr,
779 			    gettext("%s: DKIOCSGEOM failed, "), myname);
780 			perror(nullstring);
781 
782 			/* restore the default characteristics */
783 			restore_default_chars(fd, save_fdchar, save_allmap);
784 
785 			exit(3);
786 		}
787 	}
788 
789 	cyl_size = 2 * sec_size * spt;
790 
791 	if ((ibuf1 = (uchar_t *)malloc((size_t)cyl_size)) == 0 ||
792 	    (obuf = (uchar_t *)malloc((size_t)cyl_size)) == 0) {
793 		(void) fprintf(stderr,
794 		    gettext("%s: can't malloc verify buffer, "),
795 		    myname);
796 		perror(nullstring);
797 		/* restore the default characteristics */
798 		restore_default_chars(fd, save_fdchar, save_allmap);
799 
800 		exit(4);
801 	}
802 	(void) memset(ibuf1, (uchar_t)0xA5, cyl_size);
803 
804 	if (x_flag)
805 		goto skipformat;
806 
807 	if (!(q_flag && f_flag)) {
808 		if (interleave != 1) {
809 			(void) printf(gettext(
810 "Formatting %s, %d cylinders, %d sectors per trk, interleave=%d in %s\n"),
811 			    capacity, num_cyl, spt, interleave, real_name);
812 		} else {
813 			(void) printf(gettext("Formatting %s in %s\n"),
814 			    capacity, real_name);
815 		}
816 	}
817 	if (!f_flag) {
818 		(void) printf(
819 		    gettext("Press return to start formatting floppy."));
820 		while (getchar() != '\n')
821 			;
822 	}
823 	/*
824 	 * for those systems that support this ioctl, they will
825 	 * return whether or not a diskette is in the drive.
826 	 */
827 	if (ioctl(fd, FDGETCHANGE, &chgd) == 0) {
828 		if (chgd & FDGC_CURRENT) {
829 			(void) fprintf(stderr,
830 			    gettext("%s: no diskette in drive %s\n"),
831 			    myname, real_name);
832 
833 			/* restore the default characteristics */
834 			restore_default_chars(fd, save_fdchar, save_allmap);
835 
836 			exit(4);
837 		}
838 		if (chgd & FDGC_CURWPROT) {
839 			(void) fprintf(stderr,
840 			    gettext("%s: \"%s\" is write protected\n"),
841 			    myname, real_name);
842 
843 			/* restore the default characteristics */
844 			restore_default_chars(fd, save_fdchar, save_allmap);
845 
846 			exit(1);
847 		}
848 	}
849 
850 	if ((fbuf = (uchar_t *)malloc((unsigned)(4 * spt))) == 0) {
851 		(void) fprintf(stderr,
852 		    gettext("%s: can't malloc format header buffer, "),
853 		    myname);
854 		perror(nullstring);
855 
856 		/* restore the default characteristics */
857 		restore_default_chars(fd, save_fdchar, save_allmap);
858 
859 		exit(3);
860 	}
861 	/*
862 	 * do the format, a track at a time
863 	 */
864 	for (cyl = 0; cyl < (z_flag ? 1 : (int)num_cyl); cyl++) {
865 		/*
866 		 * This is not the optimal ioctl to format the floppy.
867 		 * The device driver should do do the work,
868 		 * instead of this program mucking with a lot
869 		 * of low-level, device-dependent code.
870 		 */
871 		fdr_seek.fdr_cmd[2] = cyl;
872 		if (ioctl(fd, FDRAW, &fdr_seek) == -1) {
873 			(void) fprintf(stderr,
874 			    gettext("%s: seek to cyl %d failed\n"),
875 			    myname, cyl);
876 
877 			/* restore the default characteristics */
878 			restore_default_chars(fd, save_fdchar, save_allmap);
879 
880 			exit(3);
881 		}
882 		/*
883 		 * Assume that the fd driver has issued a SENSE_INT
884 		 * command to complete the seek operation.
885 		 */
886 		for (hd = 0; hd < fdchar.fdc_nhead; hd++) {
887 			p = (uchar_t *)fbuf;
888 			for (i = 1; i <= spt; i++) {
889 				*p++ = cyl;
890 				*p++ = hd;
891 				*p++ = i; /* sector # */
892 				*p++ = (sec_size == 1024) ? 3 : 2;
893 			}
894 			/*
895 			 * ASSUME the fd driver is going to set drive-select
896 			 * bits in the second command byte
897 			 */
898 			fdr_form.fdr_cmd[1] = hd << 2;
899 			fdr_form.fdr_cmd[2] = (sec_size == 1024) ? 3 : 2;
900 			fdr_form.fdr_cmd[3] = spt;
901 			fdr_form.fdr_cmd[4] = gap;
902 			fdr_form.fdr_nbytes = 4 * spt;
903 			fdr_form.fdr_addr = (char *)fbuf;
904 
905 			if (ioctl(fd, FDRAW, &fdr_form) == -1) {
906 
907 
908 				(void) fprintf(stderr, gettext(
909 				    "%s: format of cyl %d head %d failed\n"),
910 				    myname, cyl, hd);
911 
912 				/* restore the default characteristics */
913 				restore_default_chars(fd, save_fdchar,
914 						    save_allmap);
915 
916 				exit(3);
917 			}
918 			if (fdr_form.fdr_result[0] & 0xC0) {
919 				if (fdr_form.fdr_result[1] & 0x02) {
920 					(void) fprintf(stderr, gettext(
921 					/*CSTYLED*/
922 					"%s: diskette is write protected\n"),
923 					    myname);
924 
925 					/*
926 					 * restore the default
927 					 * characteristics
928 					 */
929 					restore_default_chars(fd, save_fdchar,
930 						    save_allmap);
931 
932 					exit(3);
933 				}
934 				(void) fprintf(stderr, gettext(
935 				    "%s: format of cyl %d head %d failed\n"),
936 				    myname, cyl, hd);
937 
938 				/* restore the default characteristics */
939 				restore_default_chars(fd, save_fdchar,
940 						    save_allmap);
941 
942 				exit(3);
943 			}
944 
945 		}
946 
947 		/*
948 		 *  do a quick verify
949 		 */
950 		if (!v_flag) {
951 			if (lseek(fd, cyl * cyl_size, 0) != cyl * cyl_size) {
952 				(void) fprintf(stderr,
953 				    gettext("%s: bad seek to format verify, "),
954 				    myname);
955 				perror(nullstring);
956 				/* restore the default characteristics */
957 				restore_default_chars(fd, save_fdchar,
958 						    save_allmap);
959 
960 				exit(3);
961 			}
962 			if (read(fd, obuf, cyl_size) == cyl_size) {
963 				/* write some progress msg */
964 				/* when each cylinder is done. */
965 				if (!q_flag)
966 					(void) printf(".");
967 			} else {
968 				if (!q_flag)
969 					(void) printf(gettext("e\n"));
970 				(void) fprintf(stderr, gettext(
971 				    "%s: can't read format data, "), myname);
972 				perror(nullstring);
973 				/* restore the default characteristics */
974 				restore_default_chars(fd, save_fdchar,
975 						    save_allmap);
976 
977 				exit(3);
978 			}
979 		} else
980 			if (!q_flag)
981 				(void) printf(".");
982 		if (!q_flag)
983 			(void) fflush(stdout);
984 	}
985 	if (!q_flag)
986 		(void) printf("\n");
987 skipformat:
988 	if (v_flag) {
989 		/*
990 		 *  do a write & read verify of the entire diskette
991 		 */
992 		if (!q_flag && x_flag)
993 			(void) printf(gettext("Verifying %s in %s\n"),
994 			    capacity, real_name);
995 
996 		for (cyl = 0; cyl < (int)num_cyl; cyl++) {
997 
998 			int val;
999 			if ((val = verify(fd, 2 * spt * cyl, cyl_size)) != 0) {
1000 				perror(nullstring);
1001 
1002 				/* restore the default characteristics */
1003 				restore_default_chars(fd, save_fdchar,
1004 						save_allmap);
1005 
1006 				exit(val);
1007 
1008 			}
1009 			/* write some progress msg as */
1010 			/* each cylinder is done. */
1011 			if (!q_flag)
1012 				(void) printf(gettext("v"));
1013 			(void) fflush(stdout);
1014 		}
1015 		if (!q_flag)
1016 			(void) printf("\n");
1017 	}
1018 
1019 	if (lseek(fd, (off_t)0, 0) != 0) {
1020 		(void) fprintf(stderr, gettext("%s: seek to blk 0 failed, "),
1021 		    myname);
1022 		perror(nullstring);
1023 		/* restore the default characteristics */
1024 		restore_default_chars(fd, save_fdchar, save_allmap);
1025 
1026 		exit(3);
1027 	}
1028 
1029 }
1030 
1031 
1032 /*
1033  * Restore the default characteristics of the floppy diskette.
1034  * Fdformat changes the characteristics in the process of formatting.
1035  * If fdformat fails while in the process of doing the format, fdformat
1036  * should clean up after itself and reset the driver back to the original
1037  * state.
1038  */
1039 
1040 static void
1041 restore_default_chars(int fd,
1042 			struct fd_char save_fdchar,
1043 			struct dk_allmap save_allmap)
1044 {
1045 
1046 
1047 	/*
1048 	 * When this function is called, fdformat is failing anyways,
1049 	 * so the errors are not processed.
1050 	 */
1051 
1052 	(void) ioctl(fd, FDIOSCHAR, &save_fdchar);
1053 
1054 	(void) ioctl(fd, DKIOCSAPART, &save_allmap);
1055 
1056 	/*
1057 	 * Before looking at the diskette's characteristics, format_diskette()
1058 	 * sets the x86 floppy driver to the default characteristics.
1059 	 * restore drive to default geometry and
1060 	 * characteristics.  This ioctl isn't implemented on
1061 	 * sparc.
1062 	 */
1063 	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
1064 
1065 }
1066 
1067 /*
1068  * See if any partitions on the device are mounted.  Return 1 if a partition is
1069  * mounted.  Return 0 otherwise.
1070  */
1071 static int
1072 check_mount()
1073 {
1074 	FILE	*fp = NULL;
1075 	int	mfd;
1076 	struct dk_cinfo dkinfo_tmp;
1077 	struct mnttab   mnt_record;
1078 	struct mnttab   *mp = &mnt_record;
1079 	struct stat	stbuf;
1080 	char		raw_device[MAXPATHLEN];
1081 	int	found = 0;
1082 
1083 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1084 		perror(MNTTAB);
1085 		exit(3);
1086 	}
1087 
1088 	while (getmntent(fp, mp) == 0) {
1089 		if (strstr(mp->mnt_special, "/dev/fd") == NULL &&
1090 		    strstr(mp->mnt_special, "/dev/disket") == NULL &&
1091 		    strstr(mp->mnt_special, "/dev/c") == NULL) {
1092 			continue;
1093 		}
1094 
1095 		(void) strcpy(raw_device, "/dev/r");
1096 		(void) strcat(raw_device, mp->mnt_special + strlen("/dev/"));
1097 
1098 		/*
1099 		 * Attempt to open the device.  If it fails, skip it.
1100 		 */
1101 		if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
1102 			continue;
1103 		}
1104 
1105 		/*
1106 		 * Must be a character device
1107 		 */
1108 		if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
1109 			(void) close(mfd);
1110 			continue;
1111 		}
1112 		/*
1113 		 * Attempt to read the configuration info on the disk.
1114 		 */
1115 		if (ioctl(mfd, DKIOCINFO, &dkinfo_tmp) < 0) {
1116 			(void) close(mfd);
1117 			continue;
1118 		}
1119 		/*
1120 		 * Finished with the opened device
1121 		 */
1122 		(void) close(mfd);
1123 
1124 		/*
1125 		 * If it's not the disk we're interested in, it doesn't apply.
1126 		 */
1127 		if (dkinfo.dki_ctype != dkinfo_tmp.dki_ctype ||
1128 			dkinfo.dki_cnum != dkinfo_tmp.dki_cnum ||
1129 			dkinfo.dki_unit != dkinfo_tmp.dki_unit) {
1130 				continue;
1131 		}
1132 		/*
1133 		 * It's a mount on the disk we're checking.  If we are
1134 		 * checking whole disk, then we found trouble.  We can
1135 		 * quit searching.
1136 		 */
1137 
1138 		if (U_flag) {
1139 			if (!_dev_unmount(mp->mnt_special)) {
1140 					(void) fprintf(stderr,
1141 					gettext("%s: umount of %s failed\n"),
1142 					myname, mp->mnt_special);
1143 				found = 1;
1144 			}
1145 		} else {
1146 			(void) fprintf(stderr,
1147 				gettext("%s: %s is mounted (use -U flag)\n"),
1148 				myname, mp->mnt_special);
1149 			found = 1;
1150 		}
1151 	}
1152 	return (found);
1153 }
1154 
1155 static void
1156 usage(char *str)
1157 {
1158 char    *real_name, *alias_name;
1159 
1160 	if ((real_name = media_findname("floppy")) == NULL) {
1161 		if ((alias_name = _media_oldaliases("floppy")) != NULL)
1162 			real_name = media_findname(alias_name);
1163 	}
1164 
1165 	if (str[0] != ' ')
1166 		(void) printf("%s: %s\n", myname, str);
1167 	(void) printf(gettext(
1168 /*CSTYLED*/
1169 "\n   usage: %s [-dDeEfHlLmMqUvx] [-b label] [-B file] [-t dostype] [devname]\n"),
1170 	    myname);
1171 
1172 	(void) printf(gettext(
1173 /*CSTYLED*/
1174 	    "      -b label install \"label\" on media\n"));
1175 	(void) printf(gettext(
1176 	    "      -B file  install special boot loader on MS-DOS media\n"));
1177 	(void) printf(gettext(
1178 /*CSTYLED*/
1179 	    "      -d       format MS-DOS media\n"));
1180 	(void) printf(gettext(
1181 /*CSTYLED*/
1182 "      -D       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1183 	(void) printf(gettext(
1184 	    "      -e       eject the media when done\n"));
1185 /*CSTYLED*/
1186 	(void) printf(gettext(
1187 /*CSTYLED*/
1188 	    "      -E       format 2.88MB (3.5\") Extended-density diskette\n"));
1189 	(void) printf(gettext(
1190 	    "      -f       \"force\" - don't wait for confirmation\n"));
1191 	(void) printf(gettext(
1192 /*CSTYLED*/
1193 "      -H       format 1.44MB (3.5\") or 1.2MB (5.25\") High-density diskette\n"));
1194 	(void) printf(gettext(
1195 /*CSTYLED*/
1196 "      -l       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1197 	(void) printf(gettext(
1198 /*CSTYLED*/
1199 "      -L       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1200 	(void) printf(gettext(
1201 	    "      -m       format 1.2MB (3.5\") Medium-density diskette\n"));
1202 	(void) printf(gettext(
1203 	    "      -M       format 1.2MB (3.5\") Medium-density diskette\n"));
1204 	(void) printf(gettext(
1205 	    "      -q       quiet\n"));
1206 	(void) printf(gettext(
1207 /*CSTYLED*/
1208 	    "      -t dos   format MS-DOS media (same as -d)\n"));
1209 	(void) printf(gettext(
1210 	    "      -t nec   format NEC-DOS media (with -M only)\n"));
1211 (void) printf(gettext(
1212 /*CSTYLED*/
1213 	    "      -U       unmount media if it's mounted\n"));
1214 	(void) printf(gettext(
1215 	    "      -v       verify each block of the media\n"));
1216 	(void) printf(gettext(
1217 "      -x       skip the format, only install SunOS or DOS label\n"));
1218 
1219 	(void) printf(gettext(
1220 	    "      devname defaults to '%s'\n"),
1221 	    real_name ? real_name : gettext("no available default device"));
1222 
1223 	exit(1);
1224 
1225 }
1226 
1227 
1228 static int
1229 verify(int fd, int blk, int len)
1230 {
1231 	off_t	off;
1232 	char    *nullstring = "";
1233 
1234 	off = (off_t)(blk * (m_flag ? 1024 : 512));
1235 
1236 	if (lseek(fd, off, 0) != off) {
1237 		if (!q_flag)
1238 			(void) printf(gettext("e\n"));
1239 		(void) fprintf(stderr,
1240 		    gettext("%s: can't seek to write verify, "), myname);
1241 		perror(nullstring);
1242 		return (4);
1243 	}
1244 	if (write(fd, ibuf1, len) != len) {
1245 		if (!q_flag)
1246 			(void) printf(gettext("e\n"));
1247 		if (blk == 0)
1248 			(void) fprintf(stderr,
1249 			    gettext("%s: check diskette density, "),
1250 			    myname);
1251 		else
1252 			(void) fprintf(stderr,
1253 			    gettext("%s: can't write verify data, "),
1254 			    myname);
1255 		perror(nullstring);
1256 		return (4);
1257 	}
1258 
1259 	if (lseek(fd, off, 0) != off) {
1260 		if (!q_flag)
1261 			(void) printf(gettext("e\n"));
1262 		(void) fprintf(stderr,
1263 		    gettext("%s: bad seek to read verify, "),
1264 		    myname);
1265 		perror(nullstring);
1266 		return (4);
1267 	}
1268 	if (read(fd, obuf, len) != len) {
1269 		if (!q_flag)
1270 			(void) printf(gettext("e\n"));
1271 		(void) fprintf(stderr,
1272 		    gettext("%s: can't read verify data, "), myname);
1273 		perror(nullstring);
1274 		return (4);
1275 	}
1276 	if (memcmp(ibuf1, obuf, len)) {
1277 		if (!q_flag)
1278 			(void) printf(gettext("e\n"));
1279 		(void) fprintf(stderr, gettext("%s: verify data failure\n"),
1280 		    myname);
1281 		return (4);
1282 	}
1283 	return (0);
1284 }
1285 
1286 /*
1287  *  write a SunOS label
1288  *  NOTE:  this function assumes fd_vtoc has been filled in with the
1289  *  device specific information such as partition information
1290  *  and the asciilabel
1291  */
1292 static void
1293 write_SunOS_label(int fd, char *volname, struct vtoc *fd_vtoc)
1294 {
1295 	char    *nullstring = "";
1296 
1297 	fd_vtoc->v_sanity = VTOC_SANE;
1298 
1299 	/*
1300 	 * The label structure is set up for DEV_BSIZE (512 byte) blocks,
1301 	 * even though a medium density diskette has 1024 byte blocks
1302 	 * See dklabel.h for more details.
1303 	 */
1304 	fd_vtoc->v_sectorsz = DEV_BSIZE;
1305 
1306 	(void) strncpy(fd_vtoc->v_volume, volname, sizeof (fd_vtoc->v_volume));
1307 
1308 	/* let the fd driver finish constructing the label and writing it */
1309 	if (ioctl(fd, DKIOCSVTOC, fd_vtoc) == -1) {
1310 		(void) fprintf(stderr,
1311 		    gettext("%s: write of SunOS label failed, "), myname);
1312 		perror(nullstring);
1313 		exit(3);
1314 	}
1315 
1316 }
1317 
1318 
1319 /*
1320  *	MS-DOS Disk layout:
1321  *
1322  *	---------------------
1323  *	|    Boot sector    |
1324  *	|-------------------|
1325  *	|   Reserved area   |
1326  *	|-------------------|
1327  *	|	FAT #1      |
1328  *	|-------------------|
1329  *	|	FAT #2      |
1330  *	|-------------------|
1331  *	|   Root directory  |
1332  *	|-------------------|
1333  *	|                   |
1334  *	|     File area     |
1335  *	|___________________|
1336  */
1337 
1338 /*
1339  * The following is a copy of MS-DOS 3.3 boot block.
1340  * It consists of the BIOS parameter block, and a disk
1341  * bootstrap program.
1342  *
1343  * The BIOS parameter block contains the right values
1344  * for the 3.5" high-density 1.44MB floppy format.
1345  *
1346  */
1347 static uchar_t bootsec[512] = {
1348 	0xeb, 0x34, 0x90,	/* 8086 short jump + displacement + NOP */
1349 	'M', 'S', 'D', 'O', 'S', '3', '.', '3',	/* OEM name & version */
1350 	0, 2, 1, 1, 0,		/* Start of BIOS parameter block */
1351 	2, 224, 0, 0x40, 0xb, 0xf0, 9, 0,
1352 	18, 0, 2, 0, 0, 0,	/* End of BIOS parameter block */
1353 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1354 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
1355 	0x0, 0x0, 0x0, 0x0,
1356 	0x1, 0x0, 0xfa, 0x33,	/* 0x34, start of the bootstrap. */
1357 	0xc0, 0x8e, 0xd0, 0xbc, 0x0, 0x7c, 0x16, 0x7,
1358 	0xbb, 0x78, 0x0, 0x36, 0xc5, 0x37, 0x1e, 0x56,
1359 	0x16, 0x53, 0xbf, 0x2b, 0x7c, 0xb9, 0xb, 0x0,
1360 	0xfc, 0xac, 0x26, 0x80, 0x3d, 0x0, 0x74, 0x3,
1361 	0x26, 0x8a, 0x5, 0xaa, 0x8a, 0xc4, 0xe2, 0xf1,
1362 	0x6, 0x1f, 0x89, 0x47, 0x2, 0xc7, 0x7, 0x2b,
1363 	0x7c, 0xfb, 0xcd, 0x13, 0x72, 0x67, 0xa0, 0x10,
1364 	0x7c, 0x98, 0xf7, 0x26, 0x16, 0x7c, 0x3, 0x6,
1365 	0x1c, 0x7c, 0x3, 0x6, 0xe, 0x7c, 0xa3, 0x3f,
1366 	0x7c, 0xa3, 0x37, 0x7c, 0xb8, 0x20, 0x0, 0xf7,
1367 	0x26, 0x11, 0x7c, 0x8b, 0x1e, 0xb, 0x7c, 0x3,
1368 	0xc3, 0x48, 0xf7, 0xf3, 0x1, 0x6, 0x37, 0x7c,
1369 	0xbb, 0x0, 0x5, 0xa1, 0x3f, 0x7c, 0xe8, 0x9f,
1370 	0x0, 0xb8, 0x1, 0x2, 0xe8, 0xb3, 0x0, 0x72,
1371 	0x19, 0x8b, 0xfb, 0xb9, 0xb, 0x0, 0xbe, 0xd6,
1372 	0x7d, 0xf3, 0xa6, 0x75, 0xd, 0x8d, 0x7f, 0x20,
1373 	0xbe, 0xe1, 0x7d, 0xb9, 0xb, 0x0, 0xf3, 0xa6,
1374 	0x74, 0x18, 0xbe, 0x77, 0x7d, 0xe8, 0x6a, 0x0,
1375 	0x32, 0xe4, 0xcd, 0x16, 0x5e, 0x1f, 0x8f, 0x4,
1376 	0x8f, 0x44, 0x2, 0xcd, 0x19, 0xbe, 0xc0, 0x7d,
1377 	0xeb, 0xeb, 0xa1, 0x1c, 0x5, 0x33, 0xd2, 0xf7,
1378 	0x36, 0xb, 0x7c, 0xfe, 0xc0, 0xa2, 0x3c, 0x7c,
1379 	0xa1, 0x37, 0x7c, 0xa3, 0x3d, 0x7c, 0xbb, 0x0,
1380 	0x7, 0xa1, 0x37, 0x7c, 0xe8, 0x49, 0x0, 0xa1,
1381 	0x18, 0x7c, 0x2a, 0x6, 0x3b, 0x7c, 0x40, 0x38,
1382 	0x6, 0x3c, 0x7c, 0x73, 0x3, 0xa0, 0x3c, 0x7c,
1383 	0x50, 0xe8, 0x4e, 0x0, 0x58, 0x72, 0xc6, 0x28,
1384 	0x6, 0x3c, 0x7c, 0x74, 0xc, 0x1, 0x6, 0x37,
1385 	0x7c, 0xf7, 0x26, 0xb, 0x7c, 0x3, 0xd8, 0xeb,
1386 	0xd0, 0x8a, 0x2e, 0x15, 0x7c, 0x8a, 0x16, 0xfd,
1387 	0x7d, 0x8b, 0x1e, 0x3d, 0x7c, 0xea, 0x0, 0x0,
1388 	0x70, 0x0, 0xac, 0xa, 0xc0, 0x74, 0x22, 0xb4,
1389 	0xe, 0xbb, 0x7, 0x0, 0xcd, 0x10, 0xeb, 0xf2,
1390 	0x33, 0xd2, 0xf7, 0x36, 0x18, 0x7c, 0xfe, 0xc2,
1391 	0x88, 0x16, 0x3b, 0x7c, 0x33, 0xd2, 0xf7, 0x36,
1392 	0x1a, 0x7c, 0x88, 0x16, 0x2a, 0x7c, 0xa3, 0x39,
1393 	0x7c, 0xc3, 0xb4, 0x2, 0x8b, 0x16, 0x39, 0x7c,
1394 	0xb1, 0x6, 0xd2, 0xe6, 0xa, 0x36, 0x3b, 0x7c,
1395 	0x8b, 0xca, 0x86, 0xe9, 0x8a, 0x16, 0xfd, 0x7d,
1396 	0x8a, 0x36, 0x2a, 0x7c, 0xcd, 0x13, 0xc3, '\r',
1397 	'\n', 'N', 'o', 'n', '-', 'S', 'y', 's',
1398 	't', 'e', 'm', ' ', 'd', 'i', 's', 'k',
1399 	' ', 'o', 'r', ' ', 'd', 'i', 's', 'k',
1400 	' ', 'e', 'r', 'r', 'o', 'r', '\r', '\n',
1401 	'R', 'e', 'p', 'l', 'a', 'c', 'e', ' ',
1402 	'a', 'n', 'd', ' ', 's', 't', 'r', 'i',
1403 	'k', 'e', ' ', 'a', 'n', 'y', ' ', 'k',
1404 	'e', 'y', ' ', 'w', 'h', 'e', 'n', ' ',
1405 	'r', 'e', 'a', 'd', 'y', '\r', '\n', '\0',
1406 	'\r', '\n', 'D', 'i', 's', 'k', ' ', 'B',
1407 	'o', 'o', 't', ' ', 'f', 'a', 'i', 'l',
1408 	'u', 'r', 'e', '\r', '\n', '\0', 'I', 'O',
1409 	' ', ' ', ' ', ' ', ' ', ' ', 'S', 'Y',
1410 	'S', 'M', 'S', 'D', 'O', 'S', ' ', ' ',
1411 	' ', 'S', 'Y', 'S', '\0', 0, 0, 0,
1412 	0, 0, 0, 0, 0, 0, 0, 0, 0,
1413 	0, 0, 0, 0, 0, 0x55, 0xaa
1414 };
1415 
1416 static int
1417 valid_DOS_boot(char *bootfile, uchar_t **bootloadp)
1418 {
1419 	struct	stat status;
1420 	size_t	sizebootldr;
1421 	uchar_t	*bootloader;
1422 	int	bfd;
1423 	int	boot_size = 0;
1424 	int	err;
1425 	char	*nullstring = "";
1426 
1427 	if ((err = stat(bootfile, &status)) != 0) {
1428 		(void) fprintf(stderr, gettext("%s: \"%s\" stat error %d\n"),
1429 		    myname, bootfile, err);
1430 		return (0);
1431 	}
1432 	if ((boot_size = status.st_size) < 512) {
1433 		(void) fprintf(stderr,
1434 		    gettext("%s: short boot sector"), myname);
1435 		perror(nullstring);
1436 		return (0);
1437 	}
1438 	sizebootldr = (boot_size + 511) / 512 * 512;
1439 	if ((bootloader = (uchar_t *)malloc((size_t)sizebootldr)) == NULL) {
1440 		(void) fprintf(stderr, gettext("%s: malloc error\n"),
1441 		    myname);
1442 		return (0);
1443 	}
1444 
1445 	/* switch to user to access the boot file */
1446 	(void) seteuid(getuid());
1447 
1448 	if ((bfd = open(bootfile, O_RDONLY)) == -1) {
1449 		(void) fprintf(stderr, gettext("%s: could not open \"%s\": "),
1450 		    myname, bootfile);
1451 		perror(nullstring);
1452 		return (0);
1453 	}
1454 
1455 	/* restore effective id */
1456 	(void) seteuid(euid);
1457 
1458 	if (read(bfd, bootloader, boot_size) != boot_size) {
1459 		(void) fprintf(stderr,
1460 		    gettext("%s: read of MS-DOS boot file failed, "), myname);
1461 		perror(nullstring);
1462 		(void) close(bfd);
1463 		return (0);
1464 	}
1465 
1466 	if (!((*bootloader == 0xE9 ||
1467 	    (*bootloader == 0xEB && *(bootloader + 2) == 0x90)) &&
1468 		*(bootloader + 510) == 0x55 &&
1469 		*(bootloader + 511) == 0xAA)) {
1470 		(void) fprintf(stderr,
1471 		    gettext("%s: invalid MS-DOS boot loader image\n"), myname);
1472 		boot_size = 0;
1473 	}
1474 
1475 	(void) close(bfd);
1476 	*bootloadp = bootloader;
1477 	return (boot_size);
1478 }
1479 
1480 
1481 static void
1482 write_DOS_label(int fd, uchar_t *bootloadr, int bootlen, char *altbootname,
1483     char *doslabel, struct  bios_param_blk *bpb, int rdirsec)
1484 {
1485 	int		i, j;
1486 	int		bootclen;
1487 	size_t		fat_bsize;
1488 	ushort_t	totalsec;
1489 	uchar_t		*fat_rdir;
1490 	uchar_t		*fatptr;
1491 	char		*nullstring = "";
1492 
1493 	if (bootlen < 512 || !bootloadr) {
1494 		/* use default boot loader routine */
1495 		bootloadr = bootsec;
1496 		bootlen = 512;
1497 	} else
1498 		(void) printf
1499 			(gettext("%s: using \"%s\" for MS-DOS boot loader\n"),
1500 		    myname, altbootname);
1501 	if (bootlen % 512 > 0)
1502 		bootlen = (bootlen + 511) / 512 * 512;
1503 
1504 	bpb->b_bps[0] = getlobyte(512);
1505 	bpb->b_bps[1] = gethibyte(512);
1506 	/* MS-DOS 5.0 supports only 1 reserved sector :-( */
1507 	bpb->b_res_sec[0] = 1;
1508 	bpb->b_res_sec[1] = 0;
1509 
1510 	totalsec = fdchar.fdc_ncyl * fdchar.fdc_nhead * fdchar.fdc_secptrack;
1511 	bpb->b_totalsec[0] = getlobyte(totalsec);
1512 	bpb->b_totalsec[1] = gethibyte(totalsec);
1513 	bpb->b_spt[0] = fdchar.fdc_secptrack;
1514 	bpb->b_spt[1] = 0;
1515 	bpb->b_nhead[0] = fdchar.fdc_nhead;
1516 	bpb->b_nhead[1] = 0;
1517 	bpb->b_hiddensec[0] = 0;
1518 	bpb->b_hiddensec[1] = 0;
1519 
1520 	bpb->b_rdirents[0] = getlobyte(rdirsec);
1521 	bpb->b_rdirents[1] = gethibyte(rdirsec);
1522 
1523 	(void) memcpy((char *)(bootloadr + 0x0B), (char *)bpb,
1524 					sizeof (struct  bios_param_blk));
1525 
1526 	if (write(fd, bootloadr, 512) != 512) {
1527 		(void) fprintf(stderr,
1528 		    gettext("%s: write of MS-DOS boot sector failed"), myname);
1529 		perror(nullstring);
1530 		exit(3);
1531 	}
1532 	bootloadr += 512;
1533 	bootlen -= 512;
1534 
1535 	fat_bsize = 512 * bpb->b_fatsec[0];
1536 	fat_rdir = (uchar_t *)malloc(fat_bsize);
1537 	(void) memset(fat_rdir, (char)0, fat_bsize);
1538 
1539 	*fat_rdir = bpb->b_mediadescriptor;
1540 	*(fat_rdir + 1) = 0xFF;
1541 	*(fat_rdir + 2) = 0xFF;
1542 	bootclen = (bootlen + 512 * (int)bpb->b_spcl - 1) /
1543 	    (512 * (int)bpb->b_spcl);
1544 #define	BAD_CLUSTER 0xFF7
1545 	for (i = 0, fatptr = fat_rdir+3; i < bootclen; i++)
1546 		/*
1547 		 * pre-allocate any clusters used by boot loader if
1548 		 * loader will occupy more than 1 sector
1549 		 */
1550 		if (!(i & 01)) {
1551 			*fatptr++ = BAD_CLUSTER & 0xFF;
1552 			*fatptr = (BAD_CLUSTER >> 8) & 0x0F;
1553 		} else {
1554 			*fatptr = (*fatptr & 0x0F) |
1555 			    ((BAD_CLUSTER << 4) & 0xF0);
1556 			fatptr++;
1557 			*fatptr++ = (BAD_CLUSTER >> 4) & 0xFF;
1558 		}
1559 	for (i = 0; i < (int)bpb->b_nfat; ++i)
1560 		if (write(fd, fat_rdir, fat_bsize) != fat_bsize) {
1561 			(void) fprintf(stderr,
1562 gettext("%s: write of MS-DOS File Allocation Table failed, "),
1563 			    myname);
1564 			perror(nullstring);
1565 			exit(3);
1566 		}
1567 	rdirsec = bpb->b_rdirents[0];
1568 	rdirsec = 32 * (int)rdirsec / 512;
1569 	if (b_flag) {
1570 		struct  timeval tv;
1571 		struct	tm	*tp;
1572 		ushort_t	dostime;
1573 		ushort_t	dosday;
1574 
1575 		/* the label can be no more than 11 characters */
1576 		j = min(11, (int)strlen(doslabel));
1577 		for (i = 0; i < j; i++) {
1578 			fat_rdir[i] = uppercase(doslabel[i]);
1579 		}
1580 		for (; i < 11; i++) {
1581 			fat_rdir[i] = ' ';
1582 		}
1583 		fat_rdir[0x0B] = 0x28;
1584 		(void) gettimeofday(&tv, (struct timezone *)0);
1585 		tp = localtime(&tv.tv_sec);
1586 		/* get the time & day into DOS format */
1587 		dostime = tp->tm_sec / 2;
1588 		dostime |= tp->tm_min << 5;
1589 		dostime |= tp->tm_hour << 11;
1590 		dosday = tp->tm_mday;
1591 		dosday |= (tp->tm_mon + 1) << 5;
1592 		dosday |= (tp->tm_year - 80) << 9;
1593 		fat_rdir[0x16] = getlobyte(dostime);
1594 		fat_rdir[0x17] = gethibyte(dostime);
1595 		fat_rdir[0x18] = getlobyte(dosday);
1596 		fat_rdir[0x19] = gethibyte(dosday);
1597 
1598 		if (write(fd, fat_rdir, 512) != 512) {
1599 			(void) fprintf(stderr,
1600 			    gettext("%s: write of MS-DOS FAT failed, "),
1601 			    myname);
1602 			perror(nullstring);
1603 			exit(3);
1604 		}
1605 		i = 1;
1606 	} else {
1607 		i = 0;
1608 	}
1609 	(void) memset(fat_rdir, (char)0, 512);
1610 	for (; i < (int)rdirsec; ++i) {
1611 		if (write(fd, fat_rdir, 512) != 512) {
1612 			(void) fprintf(stderr,
1613 gettext("%s: write of MS-DOS root directory failed, "),
1614 			    myname);
1615 			perror(nullstring);
1616 			exit(3);
1617 		}
1618 	}
1619 	/*
1620 	 * Write the rest of the boot loader if it's longer than one sector.
1621 	 * The clusters used are marked Bad in the FAT.
1622 	 * No directory entry exists for this file (so that it cannot be
1623 	 * deleted).
1624 	 */
1625 	if (bootlen && write(fd, bootloadr, bootlen) != bootlen) {
1626 		(void) fprintf(stderr,
1627 		    gettext("%s: write of MS-DOS boot sectors failed"), myname);
1628 		perror(nullstring);
1629 		exit(3);
1630 	}
1631 }
1632 
1633 static void
1634 write_NEC_DOS_label(int fd, char *doslabel)
1635 {
1636 	struct		bios_param_blk *bpb;
1637 	ushort_t	fatsec;
1638 	ushort_t	rdirsec;
1639 	char		fat_rdir[1024];
1640 	int		i, j, m = 1;
1641 	uchar_t		bootsec_NEC[1024];
1642 	char		*nullstring = "";
1643 
1644 	uchar_t bios_param_NEC[30] = { 0xeb, 0x1c, 0x90, 0x0, 0x0, 0x0, 0x0,
1645 				0x0, 0x0,  0x0,  0x0, 0x0, 0x4, 0x1, 0x1, 0x0,
1646 				0x2, 0xc0, 0x0, 0xd0, 0x4, 0xfe, 0x2, 0x0,
1647 				0x8, 0x0, 0x2, 0x0, 0x0, 0x0
1648 	};
1649 
1650 	uchar_t fatdir[32] = {   0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1651 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1652 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1653 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5
1654 
1655 	};
1656 
1657 
1658 	(void) memset(bootsec_NEC, (char)0, 1024);
1659 
1660 	(void) memcpy(&bootsec_NEC, &bios_param_NEC, 30);
1661 
1662 	bpb = (struct bios_param_blk *)&(bootsec_NEC[0xb]);
1663 	if (write(fd, &bootsec_NEC[0], 1024) != 1024) {
1664 		(void) fprintf(stderr, gettext(
1665 		    "%s: write of NEC-DOS boot sector failed, "),
1666 		    myname);
1667 		perror(nullstring);
1668 		exit(3);
1669 	}
1670 	(void) memset(fat_rdir, (char)0, 1024);
1671 	fatsec = bpb->b_fatsec[0];
1672 	for (i = 0; i < (int)bpb->b_nfat * (int)fatsec; ++i) {
1673 		if ((i % (int)fatsec) == 0) {
1674 			fat_rdir[0] = bpb->b_mediadescriptor;
1675 			fat_rdir[1] = (char)0xff;
1676 			fat_rdir[2] = (char)0xff;
1677 			fat_rdir[3] = 0;
1678 			fat_rdir[4] = 0;
1679 			fat_rdir[5] = 0;
1680 		} else {
1681 			fat_rdir[0] = 0;
1682 			fat_rdir[1] = 0;
1683 			fat_rdir[2] = 0;
1684 			fat_rdir[3] = 0;
1685 			fat_rdir[4] = 0;
1686 			fat_rdir[5] = 0;
1687 		}
1688 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1689 			(void) fprintf(stderr,
1690 /*CSTYLED*/
1691 gettext("%s: write of NEC-DOS File Allocation Table failed, "), myname);
1692 			perror(nullstring);
1693 			exit(3);
1694 		}
1695 	}
1696 #ifndef	sparc
1697 	/* LINTED */
1698 	rdirsec = (int)htols(bpb->b_rdirents[0]) * 32 /1024;
1699 #else
1700 	rdirsec = (int)htols(bpb->b_rdirents[0]) * 32 /1024;
1701 #endif
1702 	if (b_flag) {
1703 		struct  timeval tv;
1704 		struct	tm	*tp;
1705 		ushort_t	dostime;
1706 		ushort_t	dosday;
1707 
1708 		/* the label can be no more than 11 characters */
1709 		j = min(11, (int)strlen(doslabel));
1710 		for (i = 0; i < j; i++) {
1711 			fat_rdir[i] = uppercase(doslabel[i]);
1712 		}
1713 		for (; i < 11; i++) {
1714 			fat_rdir[i] = ' ';
1715 		}
1716 		fat_rdir[0xb] = 0x28;
1717 		(void) gettimeofday(&tv, (struct timezone *)0);
1718 		tp = localtime(&tv.tv_sec);
1719 		/* get the time & day into DOS format */
1720 		dostime = tp->tm_sec / 2;
1721 		dostime |= tp->tm_min << 5;
1722 		dostime |= tp->tm_hour << 11;
1723 		dosday = tp->tm_mday;
1724 		dosday |= (tp->tm_mon + 1) << 5;
1725 		dosday |= (tp->tm_year - 80) << 9;
1726 		fat_rdir[0x16] = getlobyte(dostime);
1727 		fat_rdir[0x17] = gethibyte(dostime);
1728 		fat_rdir[0x18] = getlobyte(dosday);
1729 		fat_rdir[0x19] = gethibyte(dosday);
1730 
1731 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1732 			(void) fprintf(stderr,
1733 			    /*CSTYLED*/
1734 gettext("%s: write of NEC-DOS root directory failed, "), myname);
1735 			perror(nullstring);
1736 			exit(3);
1737 		}
1738 		(void) memset(fat_rdir, (char)0, 512);
1739 		i = 1;
1740 	} else {
1741 		i = 0;
1742 
1743 		while (m < 1024) {
1744 			(void) memcpy(&fat_rdir[m], &fatdir, 31);
1745 			m = m + 32;
1746 		}
1747 	}
1748 	for (; i < (int)rdirsec; ++i) {
1749 
1750 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1751 			(void) fprintf(stderr,
1752 			    /*CSTYLED*/
1753 gettext("%s: write of NEC-DOS root directory failed, "), myname);
1754 			perror(nullstring);
1755 			exit(3);
1756 		}
1757 	}
1758 }
1759