[comp.os.minix] Diskette format program

ast@cs.vu.nl (Andy Tanenbaum) (06/06/91)

echo x - format.c
sed '/^X/s///' > format.c << '/'
X/* format -  floppy disk format (PC-MINIX)          Author: D. Chapman */
X
X/* Author: Donald E. Chapman.      Copyright 1991 by Donald E. Chapman
X *
X * Usage: format [-q][-a][special [kbsize]][-v [dosvollabel]]
X *
X * Flags: -q: quiet will skip the "are you sure" prompt.
X *	  -v: volume label added along with structures Dos needs.
X *	      If no label is given format will prompt for it.
X *	  -a: sort interactive list alphabetically rather than by size.
X *
X *	     Format allows super user to format disks.  If it is suid
X *	  then it allows others to format if they are at the console.
X *	     Format will format all seven of the non-automatic
X *	  disk/media combinations that PC-Minix supports.  It will
X *	  also try to format automatics (minor 0 through 3) if a device
X *	  size was given when the node was made.  Format usually expects
X *	  special devices to have sizes.
X *	     Format will optionally add the structures Dos needs,
X *	  including a "non-bootable" message, if either the device
X *	  name is a dos device, like dosA, or if the -v flag is used.
X *	     If a special device is specified in the command line then
X *	  a size may also be specified there.  If a size is specified
X *	  in the command line the size must agree with the capability
X *	  of the special device.
X *	     If no special device is specified in the command line
X *	  format will interactively help you to determine which of
X *	  your drives you wish to format on.  It actually allows
X *	  the automatic devices to be formatted that way even if
X *	  they were made with a size of zero.
X *
X * Warning:  Some disk drives are media sensitive and on them you
X *	  should use DD or HD media as appropriate for the density
X *	  you are formatting.  For some media sensitive drives if
X *	  you try to format media of the wrong density the disk
X *	  can not be formatted correctly.
X *
X * Examples:	format /dev/at0			format disk in /dev/at0.
X *		format /dev/at0 360		format disk to size 360.
X *		format /dev/dosA		format for Dos.
X *		format /dev/fd1 -v DOS_DISK	format labeled Dos disk.
X *		format				interactive mode formatting.
X *
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <minix/config.h>
X#include <minix/const.h>
X#ifndef _V15_
X#include <minix/minlib.h>
X#endif
X#include <dirent.h>
X#include <ctype.h>
X#include <signal.h>
X#include <stdlib.h>
X#include <time.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <string.h>
X#include <stdio.h>
X
X/* Kludges */
X#ifdef _V15_
X#define mknod4 mknod
X#endif
X
X/* Macros */
X#define maj(d)  ( ((d) >> MAJOR) & BYTE)
X#define min(d)  ( ((d) >> MINOR) & 0x7F)	/* ignore format bit */
X#define illegal(s) ((s)!=360 && (s)!=720 && (s)!=1200 && (s)!=1440)
X
X/* For the list of existing drives */
Xstruct drive {
X  dev_t device;
X  int dos_type;
X  int is_auto;
X  off_t msize;
X  char *name;
X  struct drive *next;
X};
X
Xstruct param_struct {		/* Must align with diskparm.h */
X  unsigned char s1;
X  unsigned char s2;
X  unsigned char motor_off;
X  unsigned char sec_size_code;	/* Needed */
X  unsigned char sec_per_cyl;	/* Needed */
X  unsigned char gap;
X  unsigned char data_len;
X  unsigned char gap_fmt;	/* Needed */
X  unsigned char fill_byte;	/* Needed */
X  unsigned char head_settle;
X  unsigned char motor_start;
X};
X
Xstruct table_entry {
X  unsigned char cyl;
X  unsigned char head;
X  unsigned char sector;
X  unsigned char sec_size_code;
X};
X
X/* Some constants */
X#define NULL_L		(struct drive *)NULL
X#define NULL_D		(struct dirent *)NULL
X#define SEC_SIZE	 512
X#define NR_HEADS	   2
X#define F_MAJOR		   2
X#define F_DRIVE_BITS	0x03
X#define F_BIT		0x80
X#define UNLINK		TRUE
X#define OMIT		FALSE
X#define NAME_MAX	  14	/* was in limit.h strangely */
X#define PATH_MAX	 255
X#define FAIL_MOUNTED	TRUE
X#define NOT_MOUNTED	FALSE
X
X#define FAIL_OPEN	   1
X#define FAIL_SEEK	   2
X#define FAIL_WRITE	   4
X#define MAX_TRIES	  10
X
X#ifndef CONSOLE
X#define CONSOLE         "/dev/tty0"
X#endif
X
X/* Constants for DOS */
X#define BOOT_SIZE	sizeof(non_boot)
X#define VE_SIZE		sizeof(vol_entry)
X#define VEL_SIZE	sizeof(vol_entry.label)
X#define BLANKS		"           "	/* max 11 */
X#define SMALL_DIR	0x70
X#define LARGE_DIR	0xE0
X#define DIRENT_SIZE	  32
X#define VOL_ARC		0x28
X#define Y_SHIFT		   9
X#define MON_SHIFT	   5
X#define H_SHIFT		  11
X#define MIN_SHIFT	   5
X#define SEC_DIV		   2
X#define DOS_BASE_YEAR	  80
X#define FAT_SIZE	   3
X#define MAGIC_SIZE	   5
X#define MAGIC_LOC	 510
X#define NO_FAILURE	   0
X#define FAIL_D0		   1
X#define FAIL_DCLEAR	   2
X#define FAIL_DFAT2	   4
X#define FAIL_DVOL	   8
X#define FAIL_DFAT1	  16
X
Xstatic char version[] = {"format V1.1, Copyright 1991 by Donald E. Chapman"};
X
X/* Globals */
Xstruct param_struct param;
X
Xunsigned char non_boot[113] = {	/* hand constructed with loving care */
X	 0xEB, 0x50, 0x90, 0x4D, 0x49, 0x4E, 0x49, 0x58, 0x66, 0x31,
X	 0x31, 0x00, 0x02, 0x01, 0x01, 0x00, 0x02, 0xE0, 0x00, 0x60,
X	 0x09, 0xF9, 0x07, 0x00, 0x0F, 0x00, 0x02, 0x00, 0x00, 0x00,
X	 0x00, 0x00, 0x0D, 0x0A, 0x4E, 0x6F, 0x74, 0x20, 0x61, 0x20,
X	 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x20, 0x64, 0x69, 0x73,
X	 0x6B, 0x2E, 0x20, 0x52, 0x65, 0x70, 0x6C, 0x61, 0x63, 0x65,
X	 0x20, 0x61, 0x6E, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73,
X	 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 0x79, 0x2E, 0x0D,
X	 0x0A, 0x00, 0xFA, 0x33, 0xC0, 0x8E, 0xD0, 0x8E, 0xD8, 0xBC,
X	 0x00, 0x7C, 0xBE, 0x20, 0x7C, 0xFC, 0xAC, 0x3C, 0x00, 0x74,
X	 0x06, 0xB4, 0x0E, 0xCD, 0x10, 0xEB, 0xF5, 0x33, 0xC0, 0xCD,
X		       0x16, 0xCD, 0x19
X};
X
X/* Magic number for DOS and fat bytes */
Xstatic unsigned char f_bytes[5] = {0x55, 0xAA, 0xF9, 0xFF, 0xFF};
X
X/* Volume label and date */
Xstruct vol_entry {
X  char label[11];
X  unsigned char attrib;
X  char reserv[10];
X  short time;
X  short date;
X} vol_entry;
Xstatic char vol_temp[20] = {'\0'};
X
X/* Formatting work area */
Xstatic union u {
X  char buffer[BLOCK_SIZE];
X  struct p {
X	struct table_entry	/* max 128 */
X	 table[BLOCK_SIZE / 2 / sizeof(struct table_entry)];
X	struct param_struct param;
X  } p;
X} u;
X
X/* MINIX Drive/media combinations (automatics are mapped.)
X * minor 0-3 4-7  8-11 12-15  12-15  16-19  16-19 20-23  24-27  28-31
X * dtype  0   1    2     3      3      4      4     5      6      7
X * drv.  au. 360 1.2M   720   1.44M  720   1.44M   1.2M   1.2M  1.44M
X * med.  au. 360 1.2M   360    360   720    720    360    720   1.44M
X */
X
Xint dsize[] = {0, 360, 1200, 720, 720, 1200, 1200, 1440};
Xunsigned char cyl_table[] = {0, 39, 79, 39, 79, 39, 79, 79};
Xunsigned char gap_fmt_table[] = {0, 0x50, 0x54, 0x50, 0x50, 0x50, 0x50, 0x54};
Xunsigned char media_id[] = {0, 0xFD, 0xF9, 0xFD, 0xF9, 0xFD, 0xF9, 0xF0};
Xunsigned char sector_table[] = {0, 9, 15, 9, 9, 9, 9, 18};
Xunsigned char sec_cluster[] = {0, 2, 1, 2, 2, 2, 2, 1};
Xunsigned char sec_fat[] = {0, 2, 7, 2, 3, 2, 3, 9};
Xunsigned char dir_size[] = {0, SMALL_DIR, LARGE_DIR, SMALL_DIR,
X		    SMALL_DIR, SMALL_DIR, SMALL_DIR, LARGE_DIR};
X
X/* Chart of dtype mapping. 3.5 inch 1440's serve as 720's too.
X *                   dsize
X * map[u][m]    360 720 1200 1440
X * usize  360    1   3    5    3
X * usize  720    .   4    6    4
X * usize 1200    .   .    2    .
X * usize 1440    .   .    .    7
X */
Xint dtype_map[4][4] = {1, 3, 5, 3, -1, 4, 6, 4, -1, -1, 2, -1, -1, -1, -1, 7};
X
Xchar *progname, *nname;
Xint interrupt = FALSE;		/* If sigint or sigquit occurs	 */
Xint a_flag = FALSE;		/* Don't sort alphabetically */
Xint v_flag = 0;			/* Can be -1, 0 or +n */
Xint dos_type = FALSE;
Xint interactive = TRUE;
Xint max_cyl;
Xint max_len;			/* Length of longest special name */
Xint msize = 0;			/* usually holds media size in Kb */
Xint usize = 0;			/* User specified size in Kb */
Xoff_t dir_start;
Xoff_t dir_max;
Xoff_t second_fat;
X
X_PROTOTYPE(void usage, (void));
X_PROTOTYPE(void quit, (char *mess, char *more, int linked));
X_PROTOTYPE(void add_to_list, (struct drive ** h, struct drive * f));
X_PROTOTYPE(void make_list, (struct drive ** drive_list));
X_PROTOTYPE(struct drive * make_selection, (struct drive * list_head));
X_PROTOTYPE(struct drive * command_line_check, (struct drive * dp));
X_PROTOTYPE(void set_parameters, (int dtype));
X_PROTOTYPE(void adjust_bootblock, (int dtype, int msize));
X_PROTOTYPE(int mounted, (char *dname));
X_PROTOTYPE(void Sigint, (int s));
X_PROTOTYPE(void Sigquit, (int s));
X_PROTOTYPE(void prepare_node, (Dev_t device));
X_PROTOTYPE(int format_track, (int cyl, int head, int logical));
X_PROTOTYPE(int full_format, (void));
X_PROTOTYPE(int add_vol_label, (int fd));
X_PROTOTYPE(int add_structures, (char *dname, Dev_t device));
X_PROTOTYPE(int test_error, (char *dname));
X_PROTOTYPE(int main, (int argc, char *argv[]));
X
X
Xvoid usage()
X{
X  fprintf(stderr,
X	"\nUsage: %s [-q][-a][special [kbsize]][-v [dosvollabel]]\n",
X	progname);
X  exit(EXIT_FAILURE);
X}
X
Xvoid quit(mess, more, linked)
Xchar *mess;
Xchar *more;
Xint linked;
X{
X  if (linked) unlink(nname);	/* remove temporary */
X  fprintf(stderr, "\n%s: %s%s.\n", progname, mess, more);
X  exit(EXIT_FAILURE);
X}
X
Xvoid add_to_list(h, f)		/* by sizes or alphabetic */
Xstruct drive **h, *f;
X{
X  /* At the tail */
X  if ((*h) == NULL_L) {
X	f->next = (*h);
X	(*h) = f;
X  } else if (a_flag && strcmp((*h)->name, f->name) > 0 ||
X	   !a_flag && ((*h)->msize > f->msize ||
X		       (*h)->msize == f->msize && (
X					 (*h)->device > f->device ||
X					(*h)->device == f->device &&
X				       (*h)->dos_type > f->dos_type)
X		       )
X	) {
X	/* Amid the list */
X	f->next = *h;
X	(*h) = f;
X  } else			/* Recursion */
X	add_to_list(&((*h)->next), f);
X}
X
X/* Make a list of existing floppy drives found in /dev */
Xvoid make_list(drive_list)
Xstruct drive **drive_list;
X{
X  struct drive *dp;
X  DIR *dd1;
X  struct stat dstat;
X  struct dirent *dentry;
X  char *dname;
X  int sres, nlen;
X
X  max_len = 0;
X  dd1 = opendir("/dev");
X  while ((dentry = readdir(dd1)) != NULL_D) {
X	if (dentry->d_name[0] != '.') {
X		dname = (char *) malloc(strlen(dentry->d_name) + 6);
X		strcpy(dname, "/dev/");
X		strcat(dname, dentry->d_name);
X		sres = stat(dname, &dstat);
X		if (sres >= 0
X		    && S_ISBLK(dstat.st_mode)
X		    && maj(dstat.st_rdev) == F_MAJOR) {
X			dp = (struct drive *) malloc(sizeof(struct drive));
X			dp->device = dstat.st_rdev;
X			dp->msize = dstat.st_size / BLOCK_SIZE;
X			dp->name = dname;
X			nlen = strlen(dp->name);
X			if (nlen > max_len) max_len = nlen;
X			dp->dos_type = !strncmp(dentry->d_name, "dos", 3);
X			if (!((dstat.st_rdev >> 2) & 0x1F))
X				dp->is_auto = TRUE;
X			else
X				dp->is_auto = FALSE;
X			add_to_list(drive_list, dp);
X		} else
X			free(dname);	/* no leaks */
X	}
X  }
X  closedir(dd1);
X}				/* make list */
X
X/* Interactive query routine */
Xstruct drive
X*make_selection(list_head)
Xstruct drive *list_head;
X{
X  static char insize[10] = {'\0'};
X  static char choice[5] = {'\0'};
X  struct drive *dp;
X  int i, m, n, minor, drnr, dtype, fbit;
X  dev_t device;
X
X  if (list_head == NULL_L)
X	quit("Use mknod with specific minor devices first", "", OMIT);
X  printf("\nYour floppy drives:\n\n");
X  for (dp = list_head, i = 1;
X       dp != NULL_L;
X       dp = dp->next, i++) {
X	device = dp->device;
X	fbit = device & F_BIT;
X	minor = min(device);
X	drnr = minor & F_DRIVE_BITS;
X	dtype = minor >> 2;
X	msize = dp->msize;
X	if (dp->is_auto) printf(
X		       "Number %2d  %-*s     ? Kb on %4s KB drive %d%s Auto.%s\n",
X		    i, max_len, dp->name, msize ? itoa(msize) : "?",
X		       drnr, dp->dos_type ? " Dos" : "    ",
X		       fbit ? " Fbit" : "");
X	else
X		printf("Number %2d  %-*s  %4d Kb on %4d KB drive %d%s      %s\n",
X		       i, max_len, dp->name, msize, dsize[dtype],
X		       drnr, dp->dos_type ? " Dos" : "    ",
X		       fbit ? " Fbit" : "");
X  }
X  m = i - 1;
X  printf("\nWhich do you wish to format (number)? ");
X  fgets(choice, 4, stdin);
X  n = atoi(choice);
X  if (n < 1 || n > m) exit(EXIT_FAILURE);
X  for (dp = list_head, i = 1; i != n && dp != NULL_L; dp = dp->next, i++);
X  msize = dp->msize;
X  if (dp->is_auto) {
X	/* Try to determine the drive type */
X	printf("This is an automatic drive.\n");
X	printf("Format how many Kb (360 720 1200 1440) ? ");
X	fgets(insize, 7, stdin);
X	usize = atoi(insize);
X	if (illegal(msize)) {
X		printf("What is the drive size (360 720 1200 1440) ? ");
X		fgets(insize, 7, stdin);
X		msize = atoi(insize);
X	}
X  }
X  return(dp);
X}				/* make selection */
X
X/* Check the name and size from the command line */
Xstruct drive
X*command_line_check(dp)
Xstruct drive *dp;
X{
X  struct stat dstat;
X  char *ptr;
X  int fd;
X  int dtype;
X
X  if (dp == NULL_L) usage();	/* at least special */
X
X  /* Check spelling of name for DOS */
X  ptr = (char *) (dp->name + strlen(dp->name));
X  while (*ptr != '/' && ptr > dp->name) ptr--;
X  if (*ptr == '/') ptr++;
X
X  /* Also perhaps there was -v after special name */
X  dp->dos_type = dos_type || !strncmp(ptr, "dos", 3);
X  fd = open(dp->name, O_RDONLY);
X  if (fd < 0) quit("Can't open ", dp->name, OMIT);
X  if (fstat(fd, &dstat) != 0) quit("Can't stat ", dp->name, OMIT);
X  close(fd);
X  if (!S_ISBLK(dstat.st_mode) || maj(dstat.st_rdev) != F_MAJOR)
X	quit("Not a floppy ", dp->name, OMIT);
X  dp->device = dstat.st_rdev;
X  dtype = min(dp->device) >> 2;
X  msize = dp->msize = dstat.st_size / BLOCK_SIZE;
X  if (!usize) usize = msize;
X
X  /* Auto will set device as determined by usize and msize */
X  if (!dtype) {
X	if (!msize) quit("Device size unknown. Mknod with size, ",
X		     dp->name, OMIT);
X	dp->is_auto = TRUE;
X  } else {
X	dp->is_auto = FALSE;
X	if (!msize || msize != usize)
X		quit("Size error, drive is ", itoa(msize), OMIT);
X  }
X  return(dp);
X}				/* command_line_check */
X
X/* Set the values for formatting parameters */
Xvoid set_parameters(dtype)
Xint dtype;
X{
X  param.sec_size_code = (unsigned char) 2;
X  param.sec_per_cyl = sector_table[dtype];	/* sec/cyl */
X  param.gap_fmt = gap_fmt_table[dtype];
X  param.fill_byte = (unsigned char) 0xF6;	/* traditional */
X}
X
X/* Put proper values into the boot block header */
Xvoid adjust_bootblock(dtype, msize)
Xint dtype;
Xint msize;
X{
X  non_boot[0x0D] = sec_cluster[dtype];	/* sec/cluster */
X  non_boot[0x11] = dir_size[dtype];
X  non_boot[0x13] =
X	(unsigned char) (msize * (BLOCK_SIZE / SEC_SIZE)) % 256;
X  non_boot[0x14] =
X	(unsigned char) (msize * (BLOCK_SIZE / SEC_SIZE)) / 256;
X  non_boot[0x15] = media_id[dtype];	/* media id. */
X  non_boot[0x16] = sec_fat[dtype];	/* sec/fat */
X  non_boot[0x18] = sector_table[dtype];	/* sec/cyl */
X}
X
X/* See if drive is already mounted by any name. */
Xint mounted(dname)
Xchar *dname;
X{
X  int n, ddrive, mdrive;
X  struct stat dstat, mstat;
X#ifdef _V15_
X  FILE *fp;
X  char line[60],mname[20];
X#else
X  char mdevice[PATH_MAX + 1], mounted[PATH_MAX + 1], vers[5], flag[15];
X#endif
X  /* Device may be mounted under a pseudonym */
X  stat(dname, &dstat);
X  ddrive = dstat.st_rdev & F_DRIVE_BITS;
X#ifdef _V15_
X  if((fp = fopen("/etc/mtab","r")) == (FILE *) NULL) return(FAIL_MOUNTED);
X  while (TRUE) {
X      fgets(line,59,fp);
X      if(feof(fp)) break;
X      sscanf(line,"%s",mname);
X      stat(mname,&mstat);
X	if (maj(mstat.st_rdev) != F_MAJOR) continue;
X	mdrive = mstat.st_rdev & F_DRIVE_BITS;
X	if (ddrive == mdrive)
X		quit("Won't format, drive is mounted as ", mname, OMIT);
X  }
X  fclose(fp);
X  return(NOT_MOUNTED);
X#else
X  if (load_mtab(progname) < 0) return(FAIL_MOUNTED);
X  while (TRUE) {
X	n = get_mtab_entry(mdevice, mounted, vers, flag);
X	if (n < 0) return(NOT_MOUNTED);
X	stat(mdevice, &mstat);
X	if (maj(mstat.st_rdev) != F_MAJOR) continue;
X	mdrive = mstat.st_rdev & F_DRIVE_BITS;
X	if (ddrive == mdrive)
X		quit("Won't format, drive is mounted as ", mdevice, OMIT);
X  }
X#endif
X}
X
Xvoid Sigint(s)
Xint s;
X{
X  interrupt = SIGINT;
X}
X
X
Xvoid Sigquit(s)
Xint s;
X{
X  interrupt = SIGQUIT;
X}
X
X
X/* Make a temporary formatting node.  It will be deleted after use. */
Xvoid prepare_node(device)
Xdev_t device;
X{
X  int fd, mres;
X  struct stat dstat;
X  char *ptr;
X
X  /* Make a temporary name. Chop progname of excessive renaming. */
X  ptr = (char *) progname + strlen(progname);
X  while (*ptr != '/' && ptr > progname) ptr--;
X  if (*ptr == '/') ptr++;	/* Elide path */
X  nname = (char *) malloc(strlen(ptr) < 8 ? strlen(ptr) + 12 : 20);
X  strcpy(nname, "/tmp/");
X  strncat(nname, ptr, 8);
X  strcat(nname, "XXXXXX");
X  mktemp(nname);
X  mres = mknod4(nname, S_IFBLK | S_IWUSR | S_IRUSR, device | F_BIT, msize);
X  if (mres) quit("Can't make temporary node ", nname, OMIT);
X
X  /* As a pre-check open and stat the node */
X  fd = open(nname, O_WRONLY | O_EXCL);
X  if (fd < 0) quit("Can't open temporary node ", nname, UNLINK);
X  if (fstat(fd, &dstat) != 0) {
X	close(fd);
X	quit("Can't stat temporary node ", nname, UNLINK);
X  }
X  close(fd);
X}				/* prepare_node */
X
X/* Format one track */
Xint format_track(cyl, head, logical)
Xint cyl, head, logical;
X{
X  int errnum = 0;
X  int physical;
X  off_t cyl_start;
X  int fd;
X
X  /* Setup to do one head */
X  memset(u.buffer, 0, BLOCK_SIZE);
X  u.p.param = param;
X  for (physical = 0; physical <= param.sec_per_cyl; ++physical) {
X	u.p.table[physical].cyl = (unsigned char) cyl;
X	u.p.table[physical].head = (unsigned char) head;
X	u.p.table[physical].sector = (unsigned char) (physical + 1);
X	u.p.table[physical].sec_size_code = param.sec_size_code;
X  }
X
X  /* Need to set only the cylinder and head for the drive with a seek.
X   * Close enough: */
X  cyl_start = (((off_t) logical) / 2) * 2 * SEC_SIZE;
X  if ((fd = open(nname, O_WRONLY)) < 0) errnum |= FAIL_OPEN;
X  if (lseek(fd, cyl_start, SEEK_SET) != cyl_start) {
X	printf("Seek error: cyl %d head %d.\n", cyl, head);
X	errnum |= FAIL_SEEK;
X  }
X  if (write(fd, u.buffer, (unsigned) BLOCK_SIZE) != BLOCK_SIZE) {
X	printf("Write error: cyl %d head %d.\n", cyl, head);
X	errnum |= FAIL_WRITE;
X  }
X  close(fd);			/* Don't need sync. This will do. */
X  return(errnum);
X}				/* format_track */
X
X/* Format all of the tracks on the disk */
Xint full_format()
X{
X  int cyl;
X  int head;
X  int logical;
X  int errnum = 0;
X  int tries;
X
X  for (cyl = 0, logical = 1; cyl <= max_cyl && !errnum; cyl++) {
X	/* Check for interrupt */
X	if (interrupt) quit("Interrupted..", "", UNLINK);
X
X	/* Go ahead and do both heads  */
X	for (head = 0; head < NR_HEADS && !errnum; ++head) {
X		printf("\r%s: Cyl. %2d, Head %1d.  ", progname, cyl, head);
X		fflush(stdout);
X		tries = 0;
X		errnum = format_track(cyl, head, logical);
X
X		/* Were FS slots busy for other processes? */
X		while (errnum & FAIL_OPEN && tries++ < MAX_TRIES) {
X			printf("Retry: cyl %d head %d.\n", cyl, head);
X			if (interrupt) quit("Interrupted..", "", UNLINK);
X			sleep(1);
X			errnum = format_track(cyl, head, logical);
X		}
X		logical += param.sec_per_cyl;
X	}			/* for head */
X  }				/* for  cyl */
X  return(errnum);
X}				/* full_format */
X
X/* Put label and date in directory for Dos */
Xint add_vol_label(fd)
Xint fd;
X{
X  char *c;
X  time_t now;
X  struct tm *local;
X
X  /* Use a different label next time */
X  if (v_flag) v_flag = -1;
X
X  /* Massage and pad the volume label */
X  vol_temp[11] = '\0';		/* Guard */
X  c = vol_temp;
X  while (*c != '\0') {
X	if (*c == '\n' || *c == '\r') {
X		*c = '\0';
X		break;
X	} else if (!isalpha(*c) && !isdigit(*c) && *c != '.')
X		*c = '_';
X	else if (islower(*c))
X		*c = toupper(*c);
X	c++;
X  }
X  if (strlen(vol_temp) < VEL_SIZE)
X	strncat(vol_temp, BLANKS, VEL_SIZE - strlen(vol_temp));
X  strncpy(vol_entry.label, vol_temp, sizeof(vol_entry.label));
X  vol_entry.attrib = VOL_ARC;
X  time(&now);
X  local = localtime(&now);
X  vol_entry.date = (local->tm_year - DOS_BASE_YEAR) << Y_SHIFT;
X  vol_entry.date += (local->tm_mon + 1) << MON_SHIFT;
X  vol_entry.date += local->tm_mday;
X  vol_entry.time = (local->tm_hour) << H_SHIFT;
X  vol_entry.time += (local->tm_min) << MIN_SHIFT;
X  vol_entry.time += local->tm_sec / SEC_DIV;
X  if (lseek(fd, dir_start, SEEK_SET) != dir_start) return(FAIL_DVOL);
X  if (write(fd, (char *) &vol_entry, VE_SIZE) != VE_SIZE) return (FAIL_DVOL);
X  return(0);			/* No errors, errnum == 0  */
X}				/* add_vol_label */
X
X/* Add the structures Dos needs to the disk. */
Xint add_structures(dname, device)
Xchar *dname;
Xdev_t device;
X{
X  int fd;
X  int errnum;
X  off_t dos_position;
X  off_t to_do;
X  int written;
X  int do_label;
X
X  /* If special has format bit set it can't write normally */
X  if (device & F_BIT)
X	quit("Format Bit Set, Can't add structures Dos needs, rm ",
X	     dname, UNLINK);
X  printf("\nAdding structures Dos needs.  ");
X  if (interactive || v_flag < 0) {
X	printf("\nEnter Volume Label (11 or less) : ");
X	fgets(vol_temp, 19, stdin);
X	if (vol_temp[0] == '\n')
X		do_label = FALSE;
X	else
X		do_label = TRUE;
X	printf("\n");
X  }
X  if ((fd = open(dname, O_WRONLY)) < 0)
X	quit("Can't open to write structures Dos needs ",
X	     dname, UNLINK);
X
X  /* Write the boot block */
X  if (lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) return(FAIL_D0);
X  dos_position = write(fd, (char *) non_boot, BOOT_SIZE);
X  if (dos_position != (off_t) BOOT_SIZE) return(FAIL_D0);
X
X  /* Round out the first block, then zero enough of them */
X  memset(u.buffer, 0, BLOCK_SIZE);
X  to_do = (off_t) BLOCK_SIZE - dos_position;
X  while (to_do > (off_t) 0) {
X	if (interrupt) quit("Interrupted..", "", UNLINK);
X	written = write(fd, u.buffer, (int) to_do);
X	if (written != (int) to_do) return(FAIL_DCLEAR);
X	dos_position += written;
X	/* Do whole blocks */
X	to_do = dir_max - dos_position;
X	if (to_do > (off_t) BLOCK_SIZE) to_do = (off_t) BLOCK_SIZE;
X  }
X
X  /* Add volume label and date */
X  if (do_label || v_flag > 0) {
X	errnum = add_vol_label(fd);
X	if (errnum) return(errnum);
X  }
X  if (second_fat) {
X	dos_position = lseek(fd, second_fat, SEEK_SET);
X	if (dos_position != second_fat) return(FAIL_DFAT2);
X	written = write(fd, (char *) &f_bytes[2], FAT_SIZE);
X	if (written != FAT_SIZE) return(FAIL_DFAT2);
X  }
X
X  /* Do DOS magic number and first fat */
X  dos_position = lseek(fd, (off_t) MAGIC_LOC, SEEK_SET);
X  if (dos_position != (off_t) MAGIC_LOC) return(FAIL_DFAT1);
X  written = write(fd, (char *) f_bytes, MAGIC_SIZE);
X  if (written != MAGIC_SIZE) return(FAIL_DFAT1);
X  close(fd);			/* close dos structure device */
X  return(NO_FAILURE);
X}				/* add_structures */
X
X/* Try to read block 0 from the disk */
Xint test_error(dname)
Xchar *dname;
X{
X  int fd;
X  if ((fd = open(dname, O_RDONLY)) < 0) return(TRUE);
X  if (lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
X	close(fd);
X	return(TRUE);
X  }
X  if (read(fd, u.buffer, BLOCK_SIZE) != BLOCK_SIZE) {
X	close(fd);
X	return(TRUE);
X  }
X  close(fd);
X  return(FALSE);
X}
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[]; {
X
X  int q_flag = FALSE;
X  char *dname;
X  static char answer[4] = {'n'};
X  struct drive *dp = NULL_L;
X  struct drive *drive_list = NULL_L;
X  int is_auto = FALSE;
X  dev_t device;
X  int minor, dtype, drnr;
X  int i, errnum;
X
X  progname = argv[0];		/* this became a variable */
X
X/* Su should decide who is allowed to format. Normally only su should format.
X * Plucky su's could suid it however...
X */
X  if (geteuid() != 0) quit("Must be su to format", "", OMIT);
X
X  /* Normally only from the console. */
X  if (strcmp(ttyname(0), CONSOLE)) {
X	/* Suid'ed user must be at console. */
X	if (getuid() != 0) quit("Only from the console", "", OMIT);
X	/* Su can make big mistakes. :-) */
X  }
X
X  /* Get command line parameters if there are some. */
X  for (i = 1; i < argc; i++) {
X	if (argv[i][0] == '-') {
X		if (argv[i][1] == 'v') {
X			dos_type = TRUE;
X			if (argc - 1 - i > 0) {
X				v_flag = i + 1;
X				strncpy(vol_temp, argv[v_flag], 11);
X				i++;
X			} else
X				v_flag = -1;
X		}
X		if (argv[i][1] == 'q') q_flag = TRUE;
X		if (argv[i][1] == 'a') a_flag = TRUE;
X	} else if (isdigit(argv[i][0])) {
X		interactive = FALSE;
X		msize = usize = atoi(argv[i]);
X	} else if (isalpha(argv[i][0]) ||
X		   argv[i][0] == '/' ||
X		   argv[i][0] == '.') {
X		/* Name or path */
X		interactive = FALSE;
X		if (dp != NULL_L) free(dp);
X		dp = (struct drive *) malloc(sizeof(struct drive));
X		dp->name = (char *) malloc(strlen(argv[i]) + 1);
X		strcpy(dp->name, argv[i]);
X	} else
X		usage();
X  }
X
X  if (interactive) {
X	make_list(&drive_list);
X	dp = make_selection(drive_list);
X  } else
X	dp = command_line_check(dp);
X
X  if (illegal(msize)) quit("Can't do drive size ", itoa(msize), OMIT);
X
X  if (dp->is_auto) {
X	is_auto = TRUE;
X	if (illegal(usize)) quit("Can't do size ", itoa(usize), OMIT);
X	dtype = dtype_map[usize / 360 - 1][msize / 360 - 1];
X	if (dtype == -1) quit("Invalid size for drive ", dp->name, OMIT);
X	dp->device |= (dtype << 2);
X	msize = dp->msize = usize;
X  }
X  device = dp->device;
X  minor = min(device);
X  drnr = minor & F_DRIVE_BITS;
X  dtype = minor >> 2;
X  dos_type = dp->dos_type || v_flag;
X  dname = dp->name;
X
X  set_parameters(dtype);
X
X  max_cyl = cyl_table[dtype];
X
X  if (dos_type) {
X	adjust_bootblock(dtype, msize);
X	f_bytes[2] = non_boot[0x15];	/* media id. for fat */
X	second_fat = SEC_SIZE *
X		(unsigned int) non_boot[0x16] + SEC_SIZE;
X	dir_start = (off_t) non_boot[0x16] * SEC_SIZE + second_fat;
X	dir_max = (off_t) non_boot[0x11] * DIRENT_SIZE + dir_start;
X  }
X
X  /* Check /etc/mtab */
X  if (mounted(dname)) exit(EXIT_FAILURE);
X
X  /* Trap interrupts to allow cleanup of temporary node */
X  if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
X	signal(SIGINT, Sigint);
X	signal(SIGQUIT, Sigquit);
X  }
X  prepare_node(device);
X
X  if (interactive)
X	printf("%s  %s%4d Kb on %4d Kb %sdrive %d (cyl 0-%d, sec 1-%d)\n",
X	       dname, dos_type ? "Dos " : "", msize,
X	       dsize[dtype], is_auto ? "Auto. " : "", drnr,
X	       max_cyl, param.sec_per_cyl);
X
X  if (interactive || !q_flag) {
X	printf("Are you sure (y/n) ? ");
X	fgets(answer, 3, stdin);
X	if (answer[0] != 'y') {
X		unlink(nname);
X		exit(EXIT_FAILURE);
X	}
X	printf("\n");
X  }
X  do {				/* While answer == 'y' */
X
X	errnum = full_format();
X	if (errnum) quit("Format errors on ", dname, UNLINK);
X
X	if (test_error(dname))
X		quit("Wrong media? Format failed on ", dname, UNLINK);
X
X	if (dos_type) {
X		errnum = add_structures(dname, device);
X		if (errnum) quit("Failed to add structures Dos needs, ",
X			     itoa(errnum), UNLINK);
X	}
X	if (interactive || !q_flag) {
X		printf("\nAnother %s%d Kb on drive %d (y/n) ? ",
X		       dos_type ? "Dos Disk " : "", msize, drnr);
X		fgets(answer, 3, stdin);
X	} else {
X		printf("\n");
X		answer[0] = 'n';
X	}
X  } while (answer[0] == 'y');
X  unlink(nname);
X  exit(EXIT_SUCCESS);
X}
/