xref: /illumos-gate/usr/src/cmd/fdformat/fdformat.c (revision f2ba9e96867935dc624ece52573c174612f72825)
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 
473 	/* FDIOCMD ioctl command structure for formatting */
474 	/* LINTED */
475 	struct fd_cmd fcmd_fmt = {
476 		FDCMD_FORMAT_TRACK,
477 		0xA5,
478 		0,
479 		1,
480 		0,
481 		0
482 	};
483 
484 	/* FDRAW ioctl command structures for seeking and formatting */
485 	struct fd_raw fdr_seek = {
486 		FDRAW_SEEK, 0, 0, 0, 0, 0, 0, 0, 0, 0,
487 		3,
488 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489 		0,
490 		0
491 	};
492 
493 	struct fd_raw fdr_form = {
494 		0x4D, 0, 2, 0, 0x54, (char)0xA5, 0, 0, 0, 0,
495 		6,
496 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
497 		0,	/* nbytes */
498 		0	/* addr */
499 	};
500 
501 
502 	/*
503 	 * restore drive to default geometry and characteristics
504 	 * (probably not implemented on sparc)
505 	 */
506 	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
507 
508 	/* get the default partititon maps */
509 	if (ioctl(fd, DKIOCGAPART, &allmap) == -1) {
510 		(void) fprintf(stderr,
511 		    gettext("%s: DKIOCGAPART failed, "), myname);
512 		perror(nullstring);
513 		exit(3);
514 	}
515 
516 	/* Save the original default partition maps */
517 	save_allmap = allmap;
518 
519 	/* find out the characteristics of the default diskette */
520 	if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) {
521 		(void) fprintf(stderr,
522 		    gettext("%s: FDIOGCHAR failed, "), myname);
523 		perror(nullstring);
524 		exit(3);
525 	}
526 
527 	/* Save the original characteristics of the default diskette */
528 	save_fdchar = fdchar;
529 
530 	/*
531 	 * The user may only format the entire diskette.
532 	 * formatting partion a or b is not allowed
533 	 */
534 	size_of_part = allmap.dka_map[dkinfo.dki_partition].dkl_nblk
535 			* DEV_BSIZE;
536 	size_of_dev = fdchar.fdc_ncyl * fdchar.fdc_nhead
537 			* fdchar.fdc_secptrack * fdchar.fdc_sec_size;
538 
539 	if (size_of_part != size_of_dev) {
540 		(void) fprintf(stderr,
541 			/*CSTYLED*/
542 			gettext("%s: The entire diskette must be formatted. Invalid device name.\n"),
543 			myname);
544 		exit(3);
545 	}
546 
547 
548 	/* find out the geometry of the drive */
549 	if (ioctl(fd, DKIOCGGEOM, &fdgeom) == -1) {
550 		(void) fprintf(stderr,
551 		    gettext("%s: DKIOCGGEOM failed, "), myname);
552 		perror(nullstring);
553 		exit(3);
554 	}
555 
556 #ifdef sparc
557 	fdchar.fdc_medium = 3;
558 #endif
559 	if (fdchar.fdc_medium == 5)
560 		drive_size = 5;
561 	else
562 		drive_size = 3;
563 
564 	/*
565 	 * set proper density flag in case we're formating to default
566 	 * characteristics because no density switch was input
567 	 */
568 	if ((E_flag | H_flag | D_flag | m_flag) == 0) {
569 		switch (fdchar.fdc_transfer_rate) {
570 		case 1000:
571 			/* assumes only ED uses 1.0 MB/sec */
572 			E_flag++;
573 			break;
574 		case 500:
575 		default:
576 			/*
577 			 * default to HD even though High density and
578 			 * "medium" density both use 500 KB/sec
579 			 */
580 			H_flag++;
581 			break;
582 #ifndef sparc
583 		case 250:
584 			/* assumes only DD uses 250 KB/sec */
585 			D_flag++;
586 			break;
587 #endif
588 		}
589 	}
590 
591 	if (H_flag) {
592 		transfer_rate = 500;
593 		num_cyl = 80;
594 		sec_size = 512;
595 		if (drive_size == 5) {
596 			(void) strcpy(fd_vtoc->v_asciilabel,
597 				"5.25\" floppy cyl 80 alt 0 hd 2 sec 15");
598 			spt = 15;
599 			capacity = "1.2 MB";
600 		} else {
601 			(void) strcpy(fd_vtoc->v_asciilabel,
602 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 18");
603 			spt = 18;
604 			capacity = "1.44 MB";
605 		}
606 		gap = 0x54;
607 	} else if (D_flag) {
608 		transfer_rate = 250;
609 		if (drive_size == 5) {
610 			(void) strcpy(fd_vtoc->v_asciilabel,
611 				"5.25\" floppy cyl 40 alt 0 hd 2 sec 9");
612 			if (fdchar.fdc_transfer_rate == 500) {
613 				/*
614 				 * formatting a 360KB DD diskette in
615 				 * a 1.2MB drive is not a good idea
616 				 */
617 				transfer_rate = 300;
618 				fdchar.fdc_steps = 2;
619 			}
620 			num_cyl = 40;
621 			gap = 0x50;
622 			capacity = "360 KB";
623 		} else {
624 			(void) strcpy(fd_vtoc->v_asciilabel,
625 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 9");
626 			num_cyl = 80;
627 			gap = 0x54;
628 			capacity = "720 KB";
629 		}
630 		sec_size = 512;
631 		spt = 9;
632 	} else if (m_flag) {
633 #ifdef sparc
634 		transfer_rate = 500;
635 #else
636 		/*
637 		 * 416.67 KB/sec is the effective transfer rate of a "medium"
638 		 * density diskette spun at 300 rpm instead of 360 rpm
639 		 */
640 		transfer_rate = 417;
641 #endif
642 		(void) strcpy(fd_vtoc->v_asciilabel,
643 				"3.5\" floppy cyl 77 alt 0 hd 2 sec 8");
644 		num_cyl = 77;
645 		sec_size = 1024;
646 		spt = 8;
647 		gap = 0x74;
648 		capacity = "1.2 MB";
649 	} else if (E_flag) {
650 		(void) strcpy(fd_vtoc->v_asciilabel,
651 				"3.5\" floppy cyl 80 alt 0 hd 2 sec 36");
652 		transfer_rate = 1000;
653 		num_cyl = 80;
654 		sec_size = 512;
655 		spt = 36;
656 		gap = 0x54;
657 		capacity = "2.88 MB";
658 	}
659 	/*
660 	 * Medium density diskettes have 1024 byte blocks.  The dk_map
661 	 * structure in dklabel.h assumes the blocks size is DEVBSIZE (512)
662 	 * bytes.  The dkl_nblk field is in terms of DEVBSIZE byte blocks
663 	 * while the spt variable is in terms of the true block size on
664 	 * the diskette.
665 	 */
666 	if (allmap.dka_map[2].dkl_nblk !=
667 			(2 * num_cyl * spt * (m_flag ? 2 : 1))) {
668 		allmap.dka_map[1].dkl_cylno = num_cyl - 1;
669 		allmap.dka_map[0].dkl_nblk = 2 * (num_cyl - 1) * spt *
670 							(m_flag ? 2 : 1);
671 		allmap.dka_map[1].dkl_nblk = 2 * spt * (m_flag ? 2 : 1);
672 		allmap.dka_map[2].dkl_nblk = 2 * num_cyl * spt *
673 							(m_flag ? 2 : 1);
674 		if (allmap.dka_map[3].dkl_nblk)
675 			allmap.dka_map[3].dkl_nblk = 2 * (num_cyl - 1) * spt *
676 							(m_flag ? 2 : 1);
677 		if (allmap.dka_map[4].dkl_nblk)
678 			allmap.dka_map[4].dkl_nblk =
679 					2 * spt * (m_flag ? 2 : 1);
680 	}
681 
682 
683 	/* initialize the vtoc structure */
684 	fd_vtoc->v_nparts = 3;
685 
686 	fd_vtoc->v_part[0].p_start = 0;
687 	fd_vtoc->v_part[0].p_size = ((num_cyl - 1) * 2 * spt *
688 							(m_flag ? 2 : 1));
689 	fd_vtoc->v_part[1].p_start = ((num_cyl - 1) * 2 * spt *
690 							(m_flag ? 2 : 1));
691 	fd_vtoc->v_part[1].p_size = 2 * spt * (m_flag ? 2 : 1);
692 
693 	fd_vtoc->v_part[2].p_start = 0;
694 	fd_vtoc->v_part[2].p_size = num_cyl * 2 * spt * (m_flag ? 2 : 1);
695 
696 	/* initialize the bios parameter blockstructure */
697 	bpb->b_nfat = 2;
698 	if (E_flag && drive_size == 3) {
699 		bpb->b_spcl = 2;
700 		*rdirsec = (ushort_t)240;
701 		bpb->b_mediadescriptor = (char)0xF0;
702 		bpb->b_fatsec[0] = 9;
703 		bpb->b_fatsec[1] = 0;
704 	} else if (H_flag) {
705 		if (drive_size == 5) {
706 			bpb->b_spcl = 1;
707 			*rdirsec = 224;
708 			bpb->b_mediadescriptor = (char)0xF9;
709 			bpb->b_fatsec[0] = 7;
710 			bpb->b_fatsec[1] = 0;
711 		} else {
712 			bpb->b_spcl = 1;
713 			*rdirsec = 224;
714 			bpb->b_mediadescriptor = (char)0xF0;
715 			bpb->b_fatsec[0] = 9;
716 			bpb->b_fatsec[1] = 0;
717 		}
718 	} else if (drive_size == 5) {
719 		bpb->b_spcl = 2;
720 		*rdirsec = 112;
721 		bpb->b_mediadescriptor = (char)0xFD;
722 		bpb->b_fatsec[0] = 2;
723 		bpb->b_fatsec[1] = 0;
724 	} else if (drive_size == 3) {
725 		bpb->b_spcl = 2;
726 		*rdirsec = 112;
727 		bpb->b_mediadescriptor = (char)0xF9;
728 		bpb->b_fatsec[0] = 3;
729 		bpb->b_fatsec[1] = 0;
730 	}
731 
732 
733 
734 #ifndef sparc
735 	if (num_cyl > fdchar.fdc_ncyl || spt > fdchar.fdc_secptrack ||
736 	    transfer_rate > fdchar.fdc_transfer_rate) {
737 		(void) fprintf(stderr,
738 		    gettext("%s: drive not capable of requested density, "),
739 		    myname);
740 		perror(nullstring);
741 		exit(3);
742 	}
743 #endif
744 	if (num_cyl != fdchar.fdc_ncyl || spt != fdchar.fdc_secptrack ||
745 	    transfer_rate != fdchar.fdc_transfer_rate) {
746 		/*
747 		 * -- CAUTION --
748 		 * The SPARC fd driver is using a non-zero value in
749 		 * fdc_medium to indicate the 360 rpm, 77 track,
750 		 * 9 sectors/track, 1024 bytes/sector mode of operation
751 		 * (similar to an 8", DS/DD, 1.2 MB floppy).
752 		 *
753 		 * The x86 fd driver uses fdc_medium as the diameter
754 		 * indicator, either 3 or 5.  It should not be modified.
755 		 */
756 #ifdef sparc
757 		fdchar.fdc_medium = m_flag ? 1 : 0;
758 #endif
759 		fdchar.fdc_transfer_rate = transfer_rate;
760 		fdchar.fdc_ncyl = num_cyl;
761 		fdchar.fdc_sec_size = sec_size;
762 		fdchar.fdc_secptrack = spt;
763 
764 		if (ioctl(fd, FDIOSCHAR, &fdchar) == -1) {
765 			(void) fprintf(stderr, gettext(
766 			    "%s: FDIOSCHAR (density selection) failed, "),
767 			    myname);
768 
769 			/* restore the default characteristics */
770 			restore_default_chars(fd, save_fdchar, save_allmap);
771 			perror(nullstring);
772 			exit(3);
773 		}
774 		if (ioctl(fd, DKIOCSAPART, &allmap) == -1) {
775 			(void) fprintf(stderr,
776 			    gettext("%s: DKIOCSAPART failed, "),
777 			    myname);
778 
779 			/* restore the default characteristics */
780 			restore_default_chars(fd, save_fdchar, save_allmap);
781 
782 			perror(nullstring);
783 			exit(3);
784 		}
785 	}
786 
787 	if (interleave != 1 && interleave != fdgeom.dkg_intrlv) {
788 		fdgeom.dkg_intrlv = interleave;
789 		if (ioctl(fd, DKIOCSGEOM, &fdgeom) == -1) {
790 			(void) fprintf(stderr,
791 			    gettext("%s: DKIOCSGEOM failed, "), myname);
792 			perror(nullstring);
793 
794 			/* restore the default characteristics */
795 			restore_default_chars(fd, save_fdchar, save_allmap);
796 
797 			exit(3);
798 		}
799 	}
800 
801 	cyl_size = 2 * sec_size * spt;
802 
803 	if ((ibuf1 = (uchar_t *)malloc((size_t)cyl_size)) == 0 ||
804 	    (obuf = (uchar_t *)malloc((size_t)cyl_size)) == 0) {
805 		(void) fprintf(stderr,
806 		    gettext("%s: can't malloc verify buffer, "),
807 		    myname);
808 		perror(nullstring);
809 		/* restore the default characteristics */
810 		restore_default_chars(fd, save_fdchar, save_allmap);
811 
812 		exit(4);
813 	}
814 	(void) memset(ibuf1, (uchar_t)0xA5, cyl_size);
815 
816 	if (x_flag)
817 		goto skipformat;
818 
819 	if (!(q_flag && f_flag))
820 		if (interleave != 1)
821 			(void) printf(gettext(
822 "Formatting %s, %d cylinders, %d sectors per trk, interleave=%d in %s\n"),
823 			    capacity, num_cyl, spt, interleave, real_name);
824 		else
825 			(void) printf(gettext("Formatting %s in %s\n"),
826 			    capacity, real_name);
827 
828 	if (!f_flag) {
829 		(void) printf(
830 		    gettext("Press return to start formatting floppy."));
831 		while (getchar() != '\n')
832 			;
833 	}
834 	/*
835 	 * for those systems that support this ioctl, they will
836 	 * return whether or not a diskette is in the drive.
837 	 */
838 	if (ioctl(fd, FDGETCHANGE, &chgd) == 0) {
839 		if (chgd & FDGC_CURRENT) {
840 			(void) fprintf(stderr,
841 			    gettext("%s: no diskette in drive %s\n"),
842 			    myname, real_name);
843 
844 			/* restore the default characteristics */
845 			restore_default_chars(fd, save_fdchar, save_allmap);
846 
847 			exit(4);
848 		}
849 		if (chgd & FDGC_CURWPROT) {
850 			(void) fprintf(stderr,
851 			    gettext("%s: \"%s\" is write protected\n"),
852 			    myname, real_name);
853 
854 			/* restore the default characteristics */
855 			restore_default_chars(fd, save_fdchar, save_allmap);
856 
857 			exit(1);
858 		}
859 	}
860 
861 	if ((fbuf = (uchar_t *)malloc((unsigned)(4 * spt))) == 0) {
862 		(void) fprintf(stderr,
863 		    gettext("%s: can't malloc format header buffer, "),
864 		    myname);
865 		perror(nullstring);
866 
867 		/* restore the default characteristics */
868 		restore_default_chars(fd, save_fdchar, save_allmap);
869 
870 		exit(3);
871 	}
872 	/*
873 	 * do the format, a track at a time
874 	 */
875 	fcmd_fmt.fdc_blkno = 0;
876 	for (cyl = 0; cyl < (z_flag ? 1 : (int)num_cyl); cyl++) {
877 #if 0
878 		/*
879 		 * This should be the ioctl used to format the floppy.
880 		 * The device driver should do do the work,
881 		 * instead of this program mucking with a lot
882 		 * of low-level, device-dependent code.
883 		 */
884 		for (hd = 0; hd < fdchar.fdc_nhead; hd++) {
885 			if (ioctl(fd, FDIOCMD, &fcmd_fmt) == -1) {
886 				(void) fprintf(stderr,
887 			gettext("%s: format of cyl %d head %d failed\n"),
888 				    myname, cyl, hd);
889 
890 				/* restore the default characteristics */
891 				restore_default_chars(fd, save_fdchar,
892 								save_allmap);
893 				exit(3);
894 			}
895 			fcmd_fmt.fdc_blkno += spt;
896 		}
897 #else
898 		/*
899 		 * This is not the optimal ioctl to format the floppy.
900 		 * The device driver should do do the work,
901 		 * instead of this program mucking with a lot
902 		 * of low-level, device-dependent code.
903 		 */
904 		fdr_seek.fdr_cmd[2] = cyl;
905 		if (ioctl(fd, FDRAW, &fdr_seek) == -1) {
906 			(void) fprintf(stderr,
907 			    gettext("%s: seek to cyl %d failed\n"),
908 			    myname, cyl);
909 
910 			/* restore the default characteristics */
911 			restore_default_chars(fd, save_fdchar, save_allmap);
912 
913 			exit(3);
914 		}
915 		/*
916 		 * Assume that the fd driver has issued a SENSE_INT
917 		 * command to complete the seek operation.
918 		 */
919 		for (hd = 0; hd < fdchar.fdc_nhead; hd++) {
920 			p = (uchar_t *)fbuf;
921 			for (i = 1; i <= spt; i++) {
922 				*p++ = cyl;
923 				*p++ = hd;
924 				*p++ = i; /* sector # */
925 				*p++ = (sec_size == 1024) ? 3 : 2;
926 			}
927 			/*
928 			 * ASSUME the fd driver is going to set drive-select
929 			 * bits in the second command byte
930 			 */
931 			fdr_form.fdr_cmd[1] = hd << 2;
932 			fdr_form.fdr_cmd[2] = (sec_size == 1024) ? 3 : 2;
933 			fdr_form.fdr_cmd[3] = spt;
934 			fdr_form.fdr_cmd[4] = gap;
935 			fdr_form.fdr_nbytes = 4 * spt;
936 			fdr_form.fdr_addr = (char *)fbuf;
937 
938 			if (ioctl(fd, FDRAW, &fdr_form) == -1) {
939 
940 
941 				(void) fprintf(stderr, gettext(
942 				    "%s: format of cyl %d head %d failed\n"),
943 				    myname, cyl, hd);
944 
945 				/* restore the default characteristics */
946 				restore_default_chars(fd, save_fdchar,
947 						    save_allmap);
948 
949 				exit(3);
950 			}
951 			if (fdr_form.fdr_result[0] & 0xC0) {
952 				if (fdr_form.fdr_result[1] & 0x02) {
953 					(void) fprintf(stderr, gettext(
954 					/*CSTYLED*/
955 					"%s: diskette is write protected\n"),
956 					    myname);
957 
958 					/*
959 					 * restore the default
960 					 * characteristics
961 					 */
962 					restore_default_chars(fd, save_fdchar,
963 						    save_allmap);
964 
965 					exit(3);
966 				}
967 				(void) fprintf(stderr, gettext(
968 				    "%s: format of cyl %d head %d failed\n"),
969 				    myname, cyl, hd);
970 
971 				/* restore the default characteristics */
972 				restore_default_chars(fd, save_fdchar,
973 						    save_allmap);
974 
975 				exit(3);
976 			}
977 
978 		}
979 #endif
980 
981 		/*
982 		 *  do a quick verify
983 		 */
984 		if (!v_flag) {
985 			if (lseek(fd, cyl * cyl_size, 0) != cyl * cyl_size) {
986 				(void) fprintf(stderr,
987 				    gettext("%s: bad seek to format verify, "),
988 				    myname);
989 				perror(nullstring);
990 				/* restore the default characteristics */
991 				restore_default_chars(fd, save_fdchar,
992 						    save_allmap);
993 
994 				exit(3);
995 			}
996 			if (read(fd, obuf, cyl_size) == cyl_size) {
997 				/* write some progress msg */
998 				/* when each cylinder is done. */
999 				if (!q_flag)
1000 					(void) printf(".");
1001 			} else {
1002 				if (!q_flag)
1003 					(void) printf(gettext("e\n"));
1004 				(void) fprintf(stderr, gettext(
1005 				    "%s: can't read format data, "), myname);
1006 				perror(nullstring);
1007 				/* restore the default characteristics */
1008 				restore_default_chars(fd, save_fdchar,
1009 						    save_allmap);
1010 
1011 				exit(3);
1012 			}
1013 		} else
1014 			if (!q_flag)
1015 				(void) printf(".");
1016 		if (!q_flag)
1017 			(void) fflush(stdout);
1018 	}
1019 	if (!q_flag)
1020 		(void) printf("\n");
1021 skipformat:
1022 	if (v_flag) {
1023 		/*
1024 		 *  do a write & read verify of the entire diskette
1025 		 */
1026 		if (!q_flag && x_flag)
1027 			(void) printf(gettext("Verifying %s in %s\n"),
1028 			    capacity, real_name);
1029 
1030 		for (cyl = 0; cyl < (int)num_cyl; cyl++) {
1031 
1032 			int val;
1033 			if ((val = verify(fd, 2 * spt * cyl, cyl_size)) != 0) {
1034 				perror(nullstring);
1035 
1036 				/* restore the default characteristics */
1037 				restore_default_chars(fd, save_fdchar,
1038 						save_allmap);
1039 
1040 				exit(val);
1041 
1042 			}
1043 			/* write some progress msg as */
1044 			/* each cylinder is done. */
1045 			if (!q_flag)
1046 				(void) printf(gettext("v"));
1047 			(void) fflush(stdout);
1048 		}
1049 		if (!q_flag)
1050 			(void) printf("\n");
1051 	}
1052 
1053 	if (lseek(fd, (off_t)0, 0) != 0) {
1054 		(void) fprintf(stderr, gettext("%s: seek to blk 0 failed, "),
1055 		    myname);
1056 		perror(nullstring);
1057 		/* restore the default characteristics */
1058 		restore_default_chars(fd, save_fdchar, save_allmap);
1059 
1060 		exit(3);
1061 	}
1062 
1063 }
1064 
1065 
1066 /*
1067  * Restore the default characteristics of the floppy diskette.
1068  * Fdformat changes the characteristics in the process of formatting.
1069  * If fdformat fails while in the process of doing the format, fdformat
1070  * should clean up after itself and reset the driver back to the original
1071  * state.
1072  */
1073 
1074 static void
1075 restore_default_chars(int fd,
1076 			struct fd_char save_fdchar,
1077 			struct dk_allmap save_allmap)
1078 {
1079 
1080 
1081 	/*
1082 	 * When this function is called, fdformat is failing anyways,
1083 	 * so the errors are not processed.
1084 	 */
1085 
1086 	(void) ioctl(fd, FDIOSCHAR, &save_fdchar);
1087 
1088 	(void) ioctl(fd, DKIOCSAPART, &save_allmap);
1089 
1090 	/*
1091 	 * Before looking at the diskette's characteristics, format_diskette()
1092 	 * sets the x86 floppy driver to the default characteristics.
1093 	 * restore drive to default geometry and
1094 	 * characteristics.  This ioctl isn't implemented on
1095 	 * sparc.
1096 	 */
1097 	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
1098 
1099 }
1100 
1101 /*
1102  * See if any partitions on the device are mounted.  Return 1 if a partition is
1103  * mounted.  Return 0 otherwise.
1104  */
1105 static int
1106 check_mount()
1107 {
1108 	FILE	*fp = NULL;
1109 	int	mfd;
1110 	struct dk_cinfo dkinfo_tmp;
1111 	struct mnttab   mnt_record;
1112 	struct mnttab   *mp = &mnt_record;
1113 	struct stat	stbuf;
1114 	char		raw_device[MAXPATHLEN];
1115 	int	found = 0;
1116 
1117 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1118 		perror(MNTTAB);
1119 		exit(3);
1120 	}
1121 
1122 	while (getmntent(fp, mp) == 0) {
1123 		if (strstr(mp->mnt_special, "/dev/fd") == NULL &&
1124 		    strstr(mp->mnt_special, "/dev/disket") == NULL &&
1125 		    strstr(mp->mnt_special, "/dev/c") == NULL) {
1126 			continue;
1127 		}
1128 
1129 		(void) strcpy(raw_device, "/dev/r");
1130 		(void) strcat(raw_device, mp->mnt_special + strlen("/dev/"));
1131 
1132 		/*
1133 		 * Attempt to open the device.  If it fails, skip it.
1134 		 */
1135 		if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
1136 			continue;
1137 		}
1138 
1139 		/*
1140 		 * Must be a character device
1141 		 */
1142 		if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
1143 			(void) close(mfd);
1144 			continue;
1145 		}
1146 		/*
1147 		 * Attempt to read the configuration info on the disk.
1148 		 */
1149 		if (ioctl(mfd, DKIOCINFO, &dkinfo_tmp) < 0) {
1150 			(void) close(mfd);
1151 			continue;
1152 		}
1153 		/*
1154 		 * Finished with the opened device
1155 		 */
1156 		(void) close(mfd);
1157 
1158 		/*
1159 		 * If it's not the disk we're interested in, it doesn't apply.
1160 		 */
1161 		if (dkinfo.dki_ctype != dkinfo_tmp.dki_ctype ||
1162 			dkinfo.dki_cnum != dkinfo_tmp.dki_cnum ||
1163 			dkinfo.dki_unit != dkinfo_tmp.dki_unit) {
1164 				continue;
1165 		}
1166 		/*
1167 		 * It's a mount on the disk we're checking.  If we are
1168 		 * checking whole disk, then we found trouble.  We can
1169 		 * quit searching.
1170 		 */
1171 
1172 		if (U_flag) {
1173 			if (!_dev_unmount(mp->mnt_special)) {
1174 					(void) fprintf(stderr,
1175 					gettext("%s: umount of %s failed\n"),
1176 					myname, mp->mnt_special);
1177 				found = 1;
1178 			}
1179 		} else {
1180 			(void) fprintf(stderr,
1181 				gettext("%s: %s is mounted (use -U flag)\n"),
1182 				myname, mp->mnt_special);
1183 			found = 1;
1184 		}
1185 	}
1186 	return (found);
1187 }
1188 
1189 static void
1190 usage(char *str)
1191 {
1192 char    *real_name, *alias_name;
1193 
1194 	if ((real_name = media_findname("floppy")) == NULL) {
1195 		if ((alias_name = _media_oldaliases("floppy")) != NULL)
1196 			real_name = media_findname(alias_name);
1197 	}
1198 
1199 	if (str[0] != ' ')
1200 		(void) printf("%s: %s\n", myname, str);
1201 	(void) printf(gettext(
1202 /*CSTYLED*/
1203 "\n   usage: %s [-dDeEfHlLmMqUvx] [-b label] [-B file] [-t dostype] [devname]\n"),
1204 	    myname);
1205 
1206 	(void) printf(gettext(
1207 /*CSTYLED*/
1208 	    "      -b label install \"label\" on media\n"));
1209 	(void) printf(gettext(
1210 	    "      -B file  install special boot loader on MS-DOS media\n"));
1211 	(void) printf(gettext(
1212 /*CSTYLED*/
1213 	    "      -d       format MS-DOS media\n"));
1214 	(void) printf(gettext(
1215 /*CSTYLED*/
1216 "      -D       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1217 	(void) printf(gettext(
1218 	    "      -e       eject the media when done\n"));
1219 /*CSTYLED*/
1220 	(void) printf(gettext(
1221 /*CSTYLED*/
1222 	    "      -E       format 2.88MB (3.5\") Extended-density diskette\n"));
1223 	(void) printf(gettext(
1224 	    "      -f       \"force\" - don't wait for confirmation\n"));
1225 	(void) printf(gettext(
1226 /*CSTYLED*/
1227 "      -H       format 1.44MB (3.5\") or 1.2MB (5.25\") High-density diskette\n"));
1228 	(void) printf(gettext(
1229 /*CSTYLED*/
1230 "      -l       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1231 	(void) printf(gettext(
1232 /*CSTYLED*/
1233 "      -L       format 720KB (3.5\") or 360KB (5.25\") Double-density diskette\n"));
1234 	(void) printf(gettext(
1235 	    "      -m       format 1.2MB (3.5\") Medium-density diskette\n"));
1236 	(void) printf(gettext(
1237 	    "      -M       format 1.2MB (3.5\") Medium-density diskette\n"));
1238 	(void) printf(gettext(
1239 	    "      -q       quiet\n"));
1240 	(void) printf(gettext(
1241 /*CSTYLED*/
1242 	    "      -t dos   format MS-DOS media (same as -d)\n"));
1243 	(void) printf(gettext(
1244 	    "      -t nec   format NEC-DOS media (with -M only)\n"));
1245 (void) printf(gettext(
1246 /*CSTYLED*/
1247 	    "      -U       unmount media if it's mounted\n"));
1248 	(void) printf(gettext(
1249 	    "      -v       verify each block of the media\n"));
1250 	(void) printf(gettext(
1251 "      -x       skip the format, only install SunOS or DOS label\n"));
1252 
1253 	(void) printf(gettext(
1254 	    "      devname defaults to '%s'\n"),
1255 	    real_name ? real_name : gettext("no available default device"));
1256 
1257 	exit(1);
1258 
1259 }
1260 
1261 
1262 static int
1263 verify(int fd, int blk, int len)
1264 {
1265 	off_t	off;
1266 	char    *nullstring = "";
1267 
1268 	off = (off_t)(blk * (m_flag ? 1024 : 512));
1269 
1270 	if (lseek(fd, off, 0) != off) {
1271 		if (!q_flag)
1272 			(void) printf(gettext("e\n"));
1273 		(void) fprintf(stderr,
1274 		    gettext("%s: can't seek to write verify, "), myname);
1275 		perror(nullstring);
1276 		return (4);
1277 	}
1278 	if (write(fd, ibuf1, len) != len) {
1279 		if (!q_flag)
1280 			(void) printf(gettext("e\n"));
1281 		if (blk == 0)
1282 			(void) fprintf(stderr,
1283 			    gettext("%s: check diskette density, "),
1284 			    myname);
1285 		else
1286 			(void) fprintf(stderr,
1287 			    gettext("%s: can't write verify data, "),
1288 			    myname);
1289 		perror(nullstring);
1290 		return (4);
1291 	}
1292 
1293 	if (lseek(fd, off, 0) != off) {
1294 		if (!q_flag)
1295 			(void) printf(gettext("e\n"));
1296 		(void) fprintf(stderr,
1297 		    gettext("%s: bad seek to read verify, "),
1298 		    myname);
1299 		perror(nullstring);
1300 		return (4);
1301 	}
1302 	if (read(fd, obuf, len) != len) {
1303 		if (!q_flag)
1304 			(void) printf(gettext("e\n"));
1305 		(void) fprintf(stderr,
1306 		    gettext("%s: can't read verify data, "), myname);
1307 		perror(nullstring);
1308 		return (4);
1309 	}
1310 	if (memcmp(ibuf1, obuf, len)) {
1311 		if (!q_flag)
1312 			(void) printf(gettext("e\n"));
1313 		(void) fprintf(stderr, gettext("%s: verify data failure\n"),
1314 		    myname);
1315 		return (4);
1316 	}
1317 	return (0);
1318 }
1319 
1320 /*
1321  *  write a SunOS label
1322  *  NOTE:  this function assumes fd_vtoc has been filled in with the
1323  *  device specific information such as partition information
1324  *  and the asciilabel
1325  */
1326 static void
1327 write_SunOS_label(int fd, char *volname, struct vtoc *fd_vtoc)
1328 {
1329 	char    *nullstring = "";
1330 
1331 	fd_vtoc->v_sanity = VTOC_SANE;
1332 
1333 	/*
1334 	 * The label structure is set up for DEV_BSIZE (512 byte) blocks,
1335 	 * even though a medium density diskette has 1024 byte blocks
1336 	 * See dklabel.h for more details.
1337 	 */
1338 	fd_vtoc->v_sectorsz = DEV_BSIZE;
1339 
1340 	(void) strncpy(fd_vtoc->v_volume, volname, sizeof (fd_vtoc->v_volume));
1341 
1342 	/* let the fd driver finish constructing the label and writing it */
1343 	if (ioctl(fd, DKIOCSVTOC, fd_vtoc) == -1) {
1344 		(void) fprintf(stderr,
1345 		    gettext("%s: write of SunOS label failed, "), myname);
1346 		perror(nullstring);
1347 		exit(3);
1348 	}
1349 
1350 }
1351 
1352 
1353 /*
1354  *	MS-DOS Disk layout:
1355  *
1356  *	---------------------
1357  *	|    Boot sector    |
1358  *	|-------------------|
1359  *	|   Reserved area   |
1360  *	|-------------------|
1361  *	|	FAT #1      |
1362  *	|-------------------|
1363  *	|	FAT #2      |
1364  *	|-------------------|
1365  *	|   Root directory  |
1366  *	|-------------------|
1367  *	|                   |
1368  *	|     File area     |
1369  *	|___________________|
1370  */
1371 
1372 /*
1373  * The following is a copy of MS-DOS 3.3 boot block.
1374  * It consists of the BIOS parameter block, and a disk
1375  * bootstrap program.
1376  *
1377  * The BIOS parameter block contains the right values
1378  * for the 3.5" high-density 1.44MB floppy format.
1379  *
1380  */
1381 static uchar_t bootsec[512] = {
1382 	0xeb, 0x34, 0x90,	/* 8086 short jump + displacement + NOP */
1383 	'M', 'S', 'D', 'O', 'S', '3', '.', '3',	/* OEM name & version */
1384 	0, 2, 1, 1, 0,		/* Start of BIOS parameter block */
1385 	2, 224, 0, 0x40, 0xb, 0xf0, 9, 0,
1386 	18, 0, 2, 0, 0, 0,	/* End of BIOS parameter block */
1387 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1388 	0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12,
1389 	0x0, 0x0, 0x0, 0x0,
1390 	0x1, 0x0, 0xfa, 0x33,	/* 0x34, start of the bootstrap. */
1391 	0xc0, 0x8e, 0xd0, 0xbc, 0x0, 0x7c, 0x16, 0x7,
1392 	0xbb, 0x78, 0x0, 0x36, 0xc5, 0x37, 0x1e, 0x56,
1393 	0x16, 0x53, 0xbf, 0x2b, 0x7c, 0xb9, 0xb, 0x0,
1394 	0xfc, 0xac, 0x26, 0x80, 0x3d, 0x0, 0x74, 0x3,
1395 	0x26, 0x8a, 0x5, 0xaa, 0x8a, 0xc4, 0xe2, 0xf1,
1396 	0x6, 0x1f, 0x89, 0x47, 0x2, 0xc7, 0x7, 0x2b,
1397 	0x7c, 0xfb, 0xcd, 0x13, 0x72, 0x67, 0xa0, 0x10,
1398 	0x7c, 0x98, 0xf7, 0x26, 0x16, 0x7c, 0x3, 0x6,
1399 	0x1c, 0x7c, 0x3, 0x6, 0xe, 0x7c, 0xa3, 0x3f,
1400 	0x7c, 0xa3, 0x37, 0x7c, 0xb8, 0x20, 0x0, 0xf7,
1401 	0x26, 0x11, 0x7c, 0x8b, 0x1e, 0xb, 0x7c, 0x3,
1402 	0xc3, 0x48, 0xf7, 0xf3, 0x1, 0x6, 0x37, 0x7c,
1403 	0xbb, 0x0, 0x5, 0xa1, 0x3f, 0x7c, 0xe8, 0x9f,
1404 	0x0, 0xb8, 0x1, 0x2, 0xe8, 0xb3, 0x0, 0x72,
1405 	0x19, 0x8b, 0xfb, 0xb9, 0xb, 0x0, 0xbe, 0xd6,
1406 	0x7d, 0xf3, 0xa6, 0x75, 0xd, 0x8d, 0x7f, 0x20,
1407 	0xbe, 0xe1, 0x7d, 0xb9, 0xb, 0x0, 0xf3, 0xa6,
1408 	0x74, 0x18, 0xbe, 0x77, 0x7d, 0xe8, 0x6a, 0x0,
1409 	0x32, 0xe4, 0xcd, 0x16, 0x5e, 0x1f, 0x8f, 0x4,
1410 	0x8f, 0x44, 0x2, 0xcd, 0x19, 0xbe, 0xc0, 0x7d,
1411 	0xeb, 0xeb, 0xa1, 0x1c, 0x5, 0x33, 0xd2, 0xf7,
1412 	0x36, 0xb, 0x7c, 0xfe, 0xc0, 0xa2, 0x3c, 0x7c,
1413 	0xa1, 0x37, 0x7c, 0xa3, 0x3d, 0x7c, 0xbb, 0x0,
1414 	0x7, 0xa1, 0x37, 0x7c, 0xe8, 0x49, 0x0, 0xa1,
1415 	0x18, 0x7c, 0x2a, 0x6, 0x3b, 0x7c, 0x40, 0x38,
1416 	0x6, 0x3c, 0x7c, 0x73, 0x3, 0xa0, 0x3c, 0x7c,
1417 	0x50, 0xe8, 0x4e, 0x0, 0x58, 0x72, 0xc6, 0x28,
1418 	0x6, 0x3c, 0x7c, 0x74, 0xc, 0x1, 0x6, 0x37,
1419 	0x7c, 0xf7, 0x26, 0xb, 0x7c, 0x3, 0xd8, 0xeb,
1420 	0xd0, 0x8a, 0x2e, 0x15, 0x7c, 0x8a, 0x16, 0xfd,
1421 	0x7d, 0x8b, 0x1e, 0x3d, 0x7c, 0xea, 0x0, 0x0,
1422 	0x70, 0x0, 0xac, 0xa, 0xc0, 0x74, 0x22, 0xb4,
1423 	0xe, 0xbb, 0x7, 0x0, 0xcd, 0x10, 0xeb, 0xf2,
1424 	0x33, 0xd2, 0xf7, 0x36, 0x18, 0x7c, 0xfe, 0xc2,
1425 	0x88, 0x16, 0x3b, 0x7c, 0x33, 0xd2, 0xf7, 0x36,
1426 	0x1a, 0x7c, 0x88, 0x16, 0x2a, 0x7c, 0xa3, 0x39,
1427 	0x7c, 0xc3, 0xb4, 0x2, 0x8b, 0x16, 0x39, 0x7c,
1428 	0xb1, 0x6, 0xd2, 0xe6, 0xa, 0x36, 0x3b, 0x7c,
1429 	0x8b, 0xca, 0x86, 0xe9, 0x8a, 0x16, 0xfd, 0x7d,
1430 	0x8a, 0x36, 0x2a, 0x7c, 0xcd, 0x13, 0xc3, '\r',
1431 	'\n', 'N', 'o', 'n', '-', 'S', 'y', 's',
1432 	't', 'e', 'm', ' ', 'd', 'i', 's', 'k',
1433 	' ', 'o', 'r', ' ', 'd', 'i', 's', 'k',
1434 	' ', 'e', 'r', 'r', 'o', 'r', '\r', '\n',
1435 	'R', 'e', 'p', 'l', 'a', 'c', 'e', ' ',
1436 	'a', 'n', 'd', ' ', 's', 't', 'r', 'i',
1437 	'k', 'e', ' ', 'a', 'n', 'y', ' ', 'k',
1438 	'e', 'y', ' ', 'w', 'h', 'e', 'n', ' ',
1439 	'r', 'e', 'a', 'd', 'y', '\r', '\n', '\0',
1440 	'\r', '\n', 'D', 'i', 's', 'k', ' ', 'B',
1441 	'o', 'o', 't', ' ', 'f', 'a', 'i', 'l',
1442 	'u', 'r', 'e', '\r', '\n', '\0', 'I', 'O',
1443 	' ', ' ', ' ', ' ', ' ', ' ', 'S', 'Y',
1444 	'S', 'M', 'S', 'D', 'O', 'S', ' ', ' ',
1445 	' ', 'S', 'Y', 'S', '\0', 0, 0, 0,
1446 	0, 0, 0, 0, 0, 0, 0, 0, 0,
1447 	0, 0, 0, 0, 0, 0x55, 0xaa
1448 };
1449 
1450 static int
1451 valid_DOS_boot(char *bootfile, uchar_t **bootloadp)
1452 {
1453 	struct	stat status;
1454 	size_t	sizebootldr;
1455 	uchar_t	*bootloader;
1456 	int	bfd;
1457 	int	boot_size = 0;
1458 	int	err;
1459 	char	*nullstring = "";
1460 
1461 	if (err = stat(bootfile, &status)) {
1462 		(void) fprintf(stderr, gettext("%s: \"%s\" stat error %d\n"),
1463 		    myname, bootfile, err);
1464 		return (0);
1465 	}
1466 	if ((boot_size = status.st_size) < 512) {
1467 		(void) fprintf(stderr,
1468 		    gettext("%s: short boot sector"), myname);
1469 		perror(nullstring);
1470 		return (0);
1471 	}
1472 	sizebootldr = (boot_size + 511) / 512 * 512;
1473 	if ((bootloader = (uchar_t *)malloc((size_t)sizebootldr)) == NULL) {
1474 		(void) fprintf(stderr, gettext("%s: malloc error\n"),
1475 		    myname);
1476 		return (0);
1477 	}
1478 
1479 	/* switch to user to access the boot file */
1480 	(void) seteuid(getuid());
1481 
1482 	if ((bfd = open(bootfile, O_RDONLY)) == -1) {
1483 		(void) fprintf(stderr, gettext("%s: could not open \"%s\": "),
1484 		    myname, bootfile);
1485 		perror(nullstring);
1486 		return (0);
1487 	}
1488 
1489 	/* restore effective id */
1490 	(void) seteuid(euid);
1491 
1492 	if (read(bfd, bootloader, boot_size) != boot_size) {
1493 		(void) fprintf(stderr,
1494 		    gettext("%s: read of MS-DOS boot file failed, "), myname);
1495 		perror(nullstring);
1496 		(void) close(bfd);
1497 		return (0);
1498 	}
1499 
1500 	if (!((*bootloader == 0xE9 ||
1501 	    (*bootloader == 0xEB && *(bootloader + 2) == 0x90)) &&
1502 		*(bootloader + 510) == 0x55 &&
1503 		*(bootloader + 511) == 0xAA)) {
1504 		(void) fprintf(stderr,
1505 		    gettext("%s: invalid MS-DOS boot loader image\n"), myname);
1506 		boot_size = 0;
1507 	}
1508 
1509 	(void) close(bfd);
1510 	*bootloadp = bootloader;
1511 	return (boot_size);
1512 }
1513 
1514 
1515 static void
1516 write_DOS_label(int fd, uchar_t *bootloadr, int bootlen, char *altbootname,
1517     char *doslabel, struct  bios_param_blk *bpb, int rdirsec)
1518 {
1519 	int		i, j;
1520 	int		bootclen;
1521 	size_t		fat_bsize;
1522 	ushort_t	totalsec;
1523 	uchar_t		*fat_rdir;
1524 	uchar_t		*fatptr;
1525 	char		*nullstring = "";
1526 
1527 	if (bootlen < 512 || !bootloadr) {
1528 		/* use default boot loader routine */
1529 		bootloadr = bootsec;
1530 		bootlen = 512;
1531 	} else
1532 		(void) printf
1533 			(gettext("%s: using \"%s\" for MS-DOS boot loader\n"),
1534 		    myname, altbootname);
1535 	if (bootlen % 512 > 0)
1536 		bootlen = (bootlen + 511) / 512 * 512;
1537 
1538 	bpb->b_bps[0] = getlobyte(512);
1539 	bpb->b_bps[1] = gethibyte(512);
1540 	/* MS-DOS 5.0 supports only 1 reserved sector :-( */
1541 	bpb->b_res_sec[0] = 1;
1542 	bpb->b_res_sec[1] = 0;
1543 
1544 	totalsec = fdchar.fdc_ncyl * fdchar.fdc_nhead * fdchar.fdc_secptrack;
1545 	bpb->b_totalsec[0] = getlobyte(totalsec);
1546 	bpb->b_totalsec[1] = gethibyte(totalsec);
1547 	bpb->b_spt[0] = fdchar.fdc_secptrack;
1548 	bpb->b_spt[1] = 0;
1549 	bpb->b_nhead[0] = fdchar.fdc_nhead;
1550 	bpb->b_nhead[1] = 0;
1551 	bpb->b_hiddensec[0] = 0;
1552 	bpb->b_hiddensec[1] = 0;
1553 
1554 	bpb->b_rdirents[0] = getlobyte(rdirsec);
1555 	bpb->b_rdirents[1] = gethibyte(rdirsec);
1556 
1557 	(void) memcpy((char *)(bootloadr + 0x0B), (char *)bpb,
1558 					sizeof (struct  bios_param_blk));
1559 
1560 	if (write(fd, bootloadr, 512) != 512) {
1561 		(void) fprintf(stderr,
1562 		    gettext("%s: write of MS-DOS boot sector failed"), myname);
1563 		perror(nullstring);
1564 		exit(3);
1565 	}
1566 	bootloadr += 512;
1567 	bootlen -= 512;
1568 
1569 	fat_bsize = 512 * bpb->b_fatsec[0];
1570 	fat_rdir = (uchar_t *)malloc(fat_bsize);
1571 	(void) memset(fat_rdir, (char)0, fat_bsize);
1572 
1573 	*fat_rdir = bpb->b_mediadescriptor;
1574 	*(fat_rdir + 1) = 0xFF;
1575 	*(fat_rdir + 2) = 0xFF;
1576 	bootclen = (bootlen + 512 * (int)bpb->b_spcl - 1) /
1577 	    (512 * (int)bpb->b_spcl);
1578 #define	BAD_CLUSTER 0xFF7
1579 	for (i = 0, fatptr = fat_rdir+3; i < bootclen; i++)
1580 		/*
1581 		 * pre-allocate any clusters used by boot loader if
1582 		 * loader will occupy more than 1 sector
1583 		 */
1584 		if (!(i & 01)) {
1585 			*fatptr++ = BAD_CLUSTER & 0xFF;
1586 			*fatptr = (BAD_CLUSTER >> 8) & 0x0F;
1587 		} else {
1588 			*fatptr = (*fatptr & 0x0F) |
1589 			    ((BAD_CLUSTER << 4) & 0xF0);
1590 			fatptr++;
1591 			*fatptr++ = (BAD_CLUSTER >> 4) & 0xFF;
1592 		}
1593 	for (i = 0; i < (int)bpb->b_nfat; ++i)
1594 		if (write(fd, fat_rdir, fat_bsize) != fat_bsize) {
1595 			(void) fprintf(stderr,
1596 gettext("%s: write of MS-DOS File Allocation Table failed, "),
1597 			    myname);
1598 			perror(nullstring);
1599 			exit(3);
1600 		}
1601 	rdirsec = bpb->b_rdirents[0];
1602 	rdirsec = 32 * (int)rdirsec / 512;
1603 	if (b_flag) {
1604 		struct  timeval tv;
1605 		struct	tm	*tp;
1606 		ushort_t	dostime;
1607 		ushort_t	dosday;
1608 
1609 		/* the label can be no more than 11 characters */
1610 		j = min(11, (int)strlen(doslabel));
1611 		for (i = 0; i < j; i++) {
1612 			fat_rdir[i] = uppercase(doslabel[i]);
1613 		}
1614 		for (; i < 11; i++) {
1615 			fat_rdir[i] = ' ';
1616 		}
1617 		fat_rdir[0x0B] = 0x28;
1618 		(void) gettimeofday(&tv, (struct timezone *)0);
1619 		tp = localtime(&tv.tv_sec);
1620 		/* get the time & day into DOS format */
1621 		dostime = tp->tm_sec / 2;
1622 		dostime |= tp->tm_min << 5;
1623 		dostime |= tp->tm_hour << 11;
1624 		dosday = tp->tm_mday;
1625 		dosday |= (tp->tm_mon + 1) << 5;
1626 		dosday |= (tp->tm_year - 80) << 9;
1627 		fat_rdir[0x16] = getlobyte(dostime);
1628 		fat_rdir[0x17] = gethibyte(dostime);
1629 		fat_rdir[0x18] = getlobyte(dosday);
1630 		fat_rdir[0x19] = gethibyte(dosday);
1631 
1632 		if (write(fd, fat_rdir, 512) != 512) {
1633 			(void) fprintf(stderr,
1634 			    gettext("%s: write of MS-DOS FAT failed, "),
1635 			    myname);
1636 			perror(nullstring);
1637 			exit(3);
1638 		}
1639 		i = 1;
1640 	} else {
1641 		i = 0;
1642 	}
1643 	(void) memset(fat_rdir, (char)0, 512);
1644 	for (; i < (int)rdirsec; ++i) {
1645 		if (write(fd, fat_rdir, 512) != 512) {
1646 			(void) fprintf(stderr,
1647 gettext("%s: write of MS-DOS root directory failed, "),
1648 			    myname);
1649 			perror(nullstring);
1650 			exit(3);
1651 		}
1652 	}
1653 	/*
1654 	 * Write the rest of the boot loader if it's longer than one sector.
1655 	 * The clusters used are marked Bad in the FAT.
1656 	 * No directory entry exists for this file (so that it cannot be
1657 	 * deleted).
1658 	 */
1659 	if (bootlen && write(fd, bootloadr, bootlen) != bootlen) {
1660 		(void) fprintf(stderr,
1661 		    gettext("%s: write of MS-DOS boot sectors failed"), myname);
1662 		perror(nullstring);
1663 		exit(3);
1664 	}
1665 }
1666 
1667 static void
1668 write_NEC_DOS_label(int fd, char *doslabel)
1669 {
1670 	struct		bios_param_blk *bpb;
1671 	ushort_t	fatsec;
1672 	ushort_t	rdirsec;
1673 	char		fat_rdir[1024];
1674 	int		i, j, m = 1;
1675 	uchar_t		bootsec_NEC[1024];
1676 	char		*nullstring = "";
1677 
1678 	uchar_t bios_param_NEC[30] = { 0xeb, 0x1c, 0x90, 0x0, 0x0, 0x0, 0x0,
1679 				0x0, 0x0,  0x0,  0x0, 0x0, 0x4, 0x1, 0x1, 0x0,
1680 				0x2, 0xc0, 0x0, 0xd0, 0x4, 0xfe, 0x2, 0x0,
1681 				0x8, 0x0, 0x2, 0x0, 0x0, 0x0
1682 	};
1683 
1684 	uchar_t fatdir[32] = {   0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1685 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1686 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
1687 			0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5
1688 
1689 	};
1690 
1691 
1692 	(void) memset(bootsec_NEC, (char)0, 1024);
1693 
1694 	(void) memcpy(&bootsec_NEC, &bios_param_NEC, 30);
1695 
1696 	bpb = (struct bios_param_blk *)&(bootsec_NEC[0xb]);
1697 	if (write(fd, &bootsec_NEC[0], 1024) != 1024) {
1698 		(void) fprintf(stderr, gettext(
1699 		    "%s: write of NEC-DOS boot sector failed, "),
1700 		    myname);
1701 		perror(nullstring);
1702 		exit(3);
1703 	}
1704 	(void) memset(fat_rdir, (char)0, 1024);
1705 	fatsec = bpb->b_fatsec[0];
1706 	for (i = 0; i < (int)bpb->b_nfat * (int)fatsec; ++i) {
1707 		if ((i % (int)fatsec) == 0) {
1708 			fat_rdir[0] = bpb->b_mediadescriptor;
1709 			fat_rdir[1] = (char)0xff;
1710 			fat_rdir[2] = (char)0xff;
1711 			fat_rdir[3] = 0;
1712 			fat_rdir[4] = 0;
1713 			fat_rdir[5] = 0;
1714 		} else {
1715 			fat_rdir[0] = 0;
1716 			fat_rdir[1] = 0;
1717 			fat_rdir[2] = 0;
1718 			fat_rdir[3] = 0;
1719 			fat_rdir[4] = 0;
1720 			fat_rdir[5] = 0;
1721 		}
1722 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1723 			(void) fprintf(stderr,
1724 /*CSTYLED*/
1725 gettext("%s: write of NEC-DOS File Allocation Table failed, "), myname);
1726 			perror(nullstring);
1727 			exit(3);
1728 		}
1729 	}
1730 #ifndef	sparc
1731 	/* LINTED */
1732 	rdirsec = (int)htols(bpb->b_rdirents[0]) * 32 /1024;
1733 #else
1734 	rdirsec = (int)htols(bpb->b_rdirents[0]) * 32 /1024;
1735 #endif
1736 	if (b_flag) {
1737 		struct  timeval tv;
1738 		struct	tm	*tp;
1739 		ushort_t	dostime;
1740 		ushort_t	dosday;
1741 
1742 		/* the label can be no more than 11 characters */
1743 		j = min(11, (int)strlen(doslabel));
1744 		for (i = 0; i < j; i++) {
1745 			fat_rdir[i] = uppercase(doslabel[i]);
1746 		}
1747 		for (; i < 11; i++) {
1748 			fat_rdir[i] = ' ';
1749 		}
1750 		fat_rdir[0xb] = 0x28;
1751 		(void) gettimeofday(&tv, (struct timezone *)0);
1752 		tp = localtime(&tv.tv_sec);
1753 		/* get the time & day into DOS format */
1754 		dostime = tp->tm_sec / 2;
1755 		dostime |= tp->tm_min << 5;
1756 		dostime |= tp->tm_hour << 11;
1757 		dosday = tp->tm_mday;
1758 		dosday |= (tp->tm_mon + 1) << 5;
1759 		dosday |= (tp->tm_year - 80) << 9;
1760 		fat_rdir[0x16] = getlobyte(dostime);
1761 		fat_rdir[0x17] = gethibyte(dostime);
1762 		fat_rdir[0x18] = getlobyte(dosday);
1763 		fat_rdir[0x19] = gethibyte(dosday);
1764 
1765 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1766 			(void) fprintf(stderr,
1767 			    /*CSTYLED*/
1768 gettext("%s: write of NEC-DOS root directory failed, "), myname);
1769 			perror(nullstring);
1770 			exit(3);
1771 		}
1772 		(void) memset(fat_rdir, (char)0, 512);
1773 		i = 1;
1774 	} else {
1775 		i = 0;
1776 
1777 		while (m < 1024) {
1778 			(void) memcpy(&fat_rdir[m], &fatdir, 31);
1779 			m = m + 32;
1780 		}
1781 	}
1782 	for (; i < (int)rdirsec; ++i) {
1783 
1784 		if (write(fd, &fat_rdir[0], 1024) != 1024) {
1785 			(void) fprintf(stderr,
1786 			    /*CSTYLED*/
1787 gettext("%s: write of NEC-DOS root directory failed, "), myname);
1788 			perror(nullstring);
1789 			exit(3);
1790 		}
1791 	}
1792 }
1793