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}
/