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 */
79static 	struct fd_char 		fdchar;
80static 	struct dk_geom 		fdgeom;
81static 	struct dk_allmap 	allmap;
82static 	struct dk_cinfo 	dkinfo;
83
84/* EXTERN */
85extern char	*optarg;
86extern int	optind;
87
88/* for verify buffers */
89static uchar_t	*ibuf1;
90static uchar_t	*obuf;
91
92static char	*myname;
93
94static 	int	fd_debug = 1;	/* 1 if debug XXX */
95static 	int	b_flag = 0;	/* install a volume label to the diskette */
96static 	int	d_flag = 0;	/* format the diskette in dos format */
97static 	int	D_flag = 0;	/* double (aka low) density flag */
98static 	int	e_flag = 0;	/* "eject" diskette when done (if supported) */
99static 	int	E_flag = 0;	/* extended density */
100static 	int	f_flag = 0;	/* "force" (no confirmation before start) */
101static 	int	H_flag = 0;	/* high density */
102static 	int	m_flag = 0;	/* medium density */
103static 	int	n_flag = 0;	/* format the diskette in NEC-DOS format */
104static 	int	q_flag = 0;	/* quiet format flag */
105static 	int	U_flag = 0;	/* automatically unmount if it's mounted */
106static 	int	v_flag = 0;	/* verify format/diskette flag */
107static 	int	x_flag = 0;	/* skip the format, only install SunOS label */
108				/* or DOS file system */
109static 	int	z_flag = 0;	/* debugging only, setting partial formatting */
110static 	int	interleave = 1;	/* interleave factor */
111
112static	uid_t	euid = 0;	/* stores effective user id */
113
114struct 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 */
131char	*_media_oldaliases(char *name);
132int	_dev_mounted(char *path);
133int	_dev_unmount(char *path);
134
135/*
136 * local functions
137 */
138static void	usage(char *);
139static int	verify(int, int, int);
140static void	write_SunOS_label(int, char *, struct vtoc *);
141static int	valid_DOS_boot(char *, uchar_t **);
142static void	write_DOS_label(int, uchar_t *, int, char *, char *,
143			struct  bios_param_blk *, int);
144static void	write_NEC_DOS_label(int, char *);
145static int	check_mount();
146static void	format_diskette(int, char *, struct vtoc *,
147				struct  bios_param_blk *,  int *);
148static void	restore_default_chars(int fd,
149				    struct fd_char save_fdchar,
150				    struct dk_allmap save_allmap);
151
152int
153main(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,
338gettext("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 */
451void
452format_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");
1021skipformat:
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
1074static void
1075restore_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 */
1105static int
1106check_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
1189static void
1190usage(char *str)
1191{
1192char    *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
1262static int
1263verify(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 */
1326static void
1327write_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 */
1381static 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
1450static int
1451valid_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
1515static void
1516write_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,
1596gettext("%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,
1647gettext("%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
1667static void
1668write_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*/
1725gettext("%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*/
1768gettext("%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*/
1787gettext("%s: write of NEC-DOS root directory failed, "), myname);
1788			perror(nullstring);
1789			exit(3);
1790		}
1791	}
1792}
1793