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