Metropoli BBS
VIEWER: srdisk.c MODE: TEXT (ASCII)
/*
**      Sizeable RAMDisk formatter
**      Version 1.10
**
**      Compiled with Turbo C++ 1.0
**
**      Released to PUBLIC DOMAIN by author Marko Kohtala 1991
**
**      Some documentation available in accompanying file SRDISK.DOC.
**      If not, contact author by sending E-mail from
**
**              Internet, Bitnet etc. to 'mkohtala@niksula.hut.fi'
**              CompuServe to '>INTERNET:mkohtala@niksula.hut.fi'
**
**      or by calling Airline QBBS, 24H, HST, V.32, V.42, MNP,
**      +358-0-8725380, and leaving mail to me, Marko Kohtala.
*/

#define VERSION "1.10"

/* History
**
** 1.00 09-06-91 Initial release
** 1.10 06-09-91 Added full support for multiple FATs. 1.00 had the /F
**               switch, but no support for it in formatter. Updated to
**               IOCTL_msg versio 1.10 so that media byte is no longer
**               used to indicate the media change but to indicate the
**               drive is RAM disk. Some minor fixes.
*/

#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <dir.h>
#include <dos.h>
#include <string.h>

/* IOCTL_msg exchanged with ramdrive (byte aligned) */
#pragma option -a-
#pragma pack(1)
const struct IOCTL_msg_hdr_s {  /* Header of the structure for comparison */
  unsigned short len;           /* The size of the structure */
  char unused;                  /* A byte to make it look like boot sector */
  char OEM[8];                  /* OEM and structure version number */
};

struct IOCTL_msg_s {            /* The whole structure */
  struct IOCTL_msg_hdr_s hdr;
  unsigned short bytes_per_sector;
  unsigned char sectors_per_cluster;
  unsigned short reserved_sectors;
  unsigned char FATs;           /* Number of FATs on disk */
  unsigned short dir_entries;   /* Root directory entries */
  unsigned short sectors;       /* Total number of sectors on disk */
  unsigned char media;          /* Identifies the media (default 0xFA) */
  unsigned short FAT_sectors;   /* Sectors in one copy of FAT */
  unsigned short disk_size;     /* Disk size in kilobytes */
  char volume[12];              /* This is returned in the Check Media call */
  unsigned char RW_access;
  signed char media_change;     /* -1 if media changed, 1 if not */
  unsigned short open_files;    /* These last two are not accepted in the */
  int (far *IO_entry)(void);    /* driver but must be sent anyway */
} IOCTL_msg;

#define READ_ACCESS  1  /* Bit masks for RW_access in IOCTL_msg_s */
#define WRITE_ACCESS 2

#pragma pack()
#pragma option -a

struct IOCTL_msg_hdr_s IOCTL_msg_hdr =
{
  sizeof (struct IOCTL_msg_s),
  0x90,
  "SRD 1.10"
};

/* Variables possibly supplied in command line */
int drive=0;
int format_f=0;       /* Nonzero if new format given */
int force_f=0;        /* Nonzero if ok to format */
enum {same, set, clear} write_f = same;    /* Write protect flag */
int disk_size=-1;     /* Size in kilobytes, -1 not set */
int sector_size=-1;   /* Sector size, -1 not set */
int cluster_size=-1;  /* Allocation unit size, -1 not set */
int dir_entries=-1;   /* Directory entries in root directory, -1 not set */
int FATs=-1;          /* FAT copies on the disk */

/*
**  SYNTAX
*/

void print_syntax(void)
{
  fprintf(stderr,
  "Syntax: SRDISK [<drive letter>[:]] [<size>] [S:<sector size>]\n"
  "\t\t[C:<cluster size>] [D:<dir entries>] [F:<FAT copies>]\n"
  "\t\t[/W[-]] [/Y]\n\n"
  "Cluster and sector sizes may be one of 128, 256, 512, 1024, 2048 or 4096.\n"
  "The following switches may remain unclear from the above:\n\n"
  "\t/W[-]          Write protect disk, /W- enables writes\n"
  "\t/Y             Yes, to all queries i.e. no talking, just do it\n"
  "\n");
}

/*
**  ERROR HANDLING FUNCTIONS
*/

void syntax(char *err)
{
  fprintf(stderr, "Syntax error: %s\n\n", err);
  print_syntax();
  exit(3);
}

void fatal(char *err)
{
  fprintf(stderr, "Fatal error: %s\n\n", err);
  exit(1);
}

void error(char *err)
{
  fprintf(stderr, "Error: %s\n\n", err);
  return;
}

/*
**  COMMAND LINE PARSER
*/

int parse_narg(char *argp, char **next)
{
  int res;
  if (*argp == ':') argp++;
  res = strtol(argp, next, 10);
  if (argp == *next) return -1;
  return res;
}

int test_sec_size(int size)
{
  switch(size) {
  case 128:
  case 256:
  case 512:
  case 1024:
  case 2048:
  case 4096:
    return 0;       /* Return 0 for good size */
  default:
    return 1;       /* Return 1 for bad size */
  }
}

void parse_cmdline(int argc, char *argv[])
{
  int arg;
  char *argp;
  for(arg=1; arg < argc; arg++) {
    argp = argv[arg];
    while(*argp) {
      if (*argp == '/' || *argp == '-') {
        argp++;
        switch(toupper(*argp++)) {
        case '?':
        case 'H':
          print_syntax();
          exit(0);
        case 'W':
          switch(*argp) {
          case '-': argp++;
                    write_f = clear;
                    break;
          case '+': argp++;
          default:  write_f = set;
          }
          break;
        case 'Y':
          force_f++;
          break;
        case 'S': /* Sector size */
          sector_size = parse_narg(argp, &argp);
          if (test_sec_size(sector_size)) syntax("Invalid sector size");
          format_f++;
          break;
        case 'C': /* Cluster size */
          cluster_size = parse_narg(argp, &argp);
          if (test_sec_size(cluster_size)) syntax("Invalid cluster size");
          format_f++;
          break;
        case 'D': /* Directory entries */
          dir_entries = parse_narg(argp, &argp);
          if (dir_entries < -1 || !dir_entries || dir_entries == 1
          || dir_entries > 1000)
            syntax("Invalid number of directory entries");
          format_f++;
          break;
        case 'F': /* FATs */
          FATs = parse_narg(argp, &argp);
          if (FATs < -1 || !FATs)
            syntax("Invalid number of FAT copies");
          format_f++;
          break;
        default:
          syntax("Unknown switch");
        }
      }
      else {
        if (*argp == ' ' || *argp == '\t') argp++;
        else if (isdigit(*argp)) {
          disk_size = strtol(argp, &argp, 10);
          if (disk_size < 0 || disk_size > 32767)
            syntax("Invalid disk size");
          format_f++;
        }
        else {
          if (drive) syntax("Unrecognised character on command line");
          drive = toupper(*argp++)-'A'+1;
          if (drive < 1 || drive > 'Z'-'A'+1) syntax("Invalid drive");
          if (*argp == ':') argp++;
        }
      }
    }
  }
}

/*
**  IOCTL messages
*/

void read_IOCTL_msg(void)
{
  if (ioctl(drive, 4, &IOCTL_msg, sizeof IOCTL_msg) != sizeof IOCTL_msg
  || memcmp(&IOCTL_msg, &IOCTL_msg_hdr, sizeof IOCTL_msg_hdr))
    fatal("Drive not proper Sizeable RAMDisk");
}

int write_IOCTL_msg(void)
{
  if (ioctl(drive, 5, &IOCTL_msg, sizeof (struct IOCTL_msg_s))
      != sizeof (struct IOCTL_msg_s))
  {
    error("RAMDisk driver won't accept new setup");
    return 1;
  }
  return 0;
}

void print_IOCTL_msg()
{
  int cluster_size = IOCTL_msg.bytes_per_sector * IOCTL_msg.sectors_per_cluster;
  int dir_sectors = IOCTL_msg.dir_entries * 32 / IOCTL_msg.bytes_per_sector;
  int clusters = (IOCTL_msg.sectors - IOCTL_msg.reserved_sectors
    - IOCTL_msg.FAT_sectors * IOCTL_msg.FATs - dir_sectors)
    / IOCTL_msg.sectors_per_cluster;

  printf("  Disk size: %iK\n"
         "  Cluster size: %i bytes\n"
         "  Sector size: %i bytes\n"
         "  Directory entries: %i\n"
         "  FAT copies: %i\n"
         "  Bytes available: %li\n"
         "  Write protection: %s\n"
         #ifdef VERBOSE
         "  Sectors: %i\n"
         "  Reserved sectors: %i\n"
         "  FAT sectors: %i\n"
         "  Directory sectors: %i\n"
         "  Sectors per cluster: %i\n"
         "  Clusters: %i\n"
         #endif
         "\n"
         ,IOCTL_msg.disk_size
         ,cluster_size
         ,IOCTL_msg.bytes_per_sector
         ,IOCTL_msg.dir_entries
         ,IOCTL_msg.FATs
         ,(long)clusters*cluster_size
         ,((IOCTL_msg.RW_access & WRITE_ACCESS) ? "OFF" : "ON")
         #ifdef VERBOSE
         ,IOCTL_msg.sectors
         ,IOCTL_msg.reserved_sectors
         ,IOCTL_msg.FAT_sectors
         ,dir_sectors
         ,IOCTL_msg.sectors_per_cluster
         ,clusters
         #endif
         );
}

/*
**  Write sectors to disk
**
**  Return 0 for success and 1 for failure
*/

int write_sector(int count, int start, void *buffer)
{
  if (!IOCTL_msg.IO_entry) fatal("Internal error writing sector");
  _BH = 1;  /* Write */
  _CX = count;
  _DX = start;
  _ES = FP_SEG(buffer);
  _DI = FP_OFF(buffer);
  return IOCTL_msg.IO_entry();
}

/*
**  INITIALIZE DRIVE
*/

void init_drive(void)
{
  read_IOCTL_msg();

  if (!force_f) {
    if (IOCTL_msg.disk_size) {
      printf("Current configuration for drive %c:\n\n", drive-1+'A');
      print_IOCTL_msg();
    }
    else
      printf("Drive %c: disabled\n\n", drive-1+'A');
  }
}

/*
**  SET WRITE PROTECT
*/

void set_write_protect()
{
  switch(write_f) {
  case set:
    IOCTL_msg.RW_access &= ~WRITE_ACCESS;
    if (!write_IOCTL_msg())
      printf("Write protect enabled\n\n");
    break;
  case clear:
    IOCTL_msg.RW_access |= WRITE_ACCESS;
    if (!write_IOCTL_msg())
      printf("Write protect disabled\n\n");
    break;
  }
}

/*
**  FORMAT DISK
*/

int getYN(void)
{
  int reply;

  if (force_f) reply = 'Y';
  else {
    do reply = toupper(getch());
    while (reply != 'Y' && reply != 'N');
  }
  printf("%c\n\n", reply);
  if (reply == 'N') return 0;
  return 1;
}

int licence_to_kill(void)
{
  if (!force_f) {
    int reply;
    printf("\aAbout to destroy all files on drive %c!\n\a"
           "Continue (Y/N) ? ", drive-1+'A');
    if (!getYN()) {
      printf("\nOperation aborted\n\n");
      return 0;
    }
  }
  return 1;
}

void format_disk(void)
{
  unsigned short old_disk_size = IOCTL_msg.disk_size;
  unsigned short FAT_sectors;
  unsigned short data_sectors;
  unsigned short system_sectors;
  unsigned short clusters;
  unsigned short dir_start;
  unsigned short dir_sectors;
  unsigned long sectors;
  ldiv_t ldivr;
  int Fsec;
  int i;
  void *sector;

  if (!force_f && IOCTL_msg.open_files) {
    error("Files open on drive");
    return;
  }

  /* Check disk configuration for changes */
  if (disk_size != -1)
    IOCTL_msg.disk_size = disk_size;

  if (cluster_size == -1) {
    cluster_size = IOCTL_msg.bytes_per_sector * IOCTL_msg.sectors_per_cluster;
  } else {
    if (cluster_size < IOCTL_msg.bytes_per_sector)
      cluster_size = IOCTL_msg.bytes_per_sector;
    IOCTL_msg.sectors_per_cluster = cluster_size / IOCTL_msg.bytes_per_sector;
  }

  if (sector_size != -1)
    IOCTL_msg.bytes_per_sector = sector_size;

  if (dir_entries != -1)
    IOCTL_msg.dir_entries = dir_entries;

  if (FATs != -1)
    IOCTL_msg.FATs = FATs;


  /* If Disk will be disabled */
  if (!IOCTL_msg.disk_size) {
    if (!old_disk_size) {
      /* If was disabled also before */
      write_IOCTL_msg();
      printf("New configuration saved for later use\n\n");
    } else {
      /* If disk now get's disabled */
      if (!licence_to_kill()) return;
      IOCTL_msg.RW_access = ~READ_ACCESS & ~WRITE_ACCESS;
      IOCTL_msg.media_change = -1;
      write_IOCTL_msg();
      printf("RAMDisk disabled\n\n");
    }
    return;
  }


  /* Count the new BPB for disk and see if it can be made */
  
  /* Make sure sectors are big enough for the disk */
  while((sectors = (long)IOCTL_msg.disk_size * 1024
        / IOCTL_msg.bytes_per_sector) > 0xFFFFL)
    IOCTL_msg.bytes_per_sector <<= 1;

  IOCTL_msg.sectors = sectors;
  if (cluster_size < IOCTL_msg.bytes_per_sector)
    cluster_size = IOCTL_msg.bytes_per_sector;
  IOCTL_msg.sectors_per_cluster = cluster_size / IOCTL_msg.bytes_per_sector;

  { div_t divr;
    divr = div(IOCTL_msg.dir_entries * 32, IOCTL_msg.bytes_per_sector);
    dir_sectors = divr.quot + (divr.rem ? 1 : 0);
  }

  system_sectors = IOCTL_msg.reserved_sectors + dir_sectors;
  data_sectors = IOCTL_msg.sectors - system_sectors;

  { ldiv_t divr;
    divr = ldiv((long)3 * data_sectors + 6 * IOCTL_msg.sectors_per_cluster,
                (long)2 * cluster_size + 3 * IOCTL_msg.FATs);
    IOCTL_msg.FAT_sectors = divr.quot + (divr.rem ? 1 : 0);
  }

  FAT_sectors = IOCTL_msg.FAT_sectors * IOCTL_msg.FATs;
  system_sectors += FAT_sectors;
  data_sectors -= FAT_sectors;

  clusters = data_sectors / IOCTL_msg.sectors_per_cluster;

  if (IOCTL_msg.sectors <= system_sectors || !clusters)
  {
    error("Impossible format for disk");
    return;
  }

  IOCTL_msg.media_change = -1;      /* The media change */
  IOCTL_msg.RW_access = (write_f == set ? 0 : WRITE_ACCESS);

  if (!force_f) {
    printf("New disk configuration:\n\n");
    print_IOCTL_msg();
  }

  if (old_disk_size && !licence_to_kill()) return;

  sector = malloc(IOCTL_msg.bytes_per_sector);
  if (!sector) fatal("Insufficient memory");

  /* Enable disk */
  IOCTL_msg.RW_access |= READ_ACCESS;
  /* Request the new disk space by sending the BPB */
  if (write_IOCTL_msg()) return;  /* Return if failed */

  /* Write the new disk */

  /* Make the boot sector */
  memset(sector, 0, IOCTL_msg.bytes_per_sector);
  memmove(sector, &IOCTL_msg, sizeof IOCTL_msg);
  *((short *)sector) = 0x1DEB;             /* Boot record JMP instruction */
  *((short *)sector+12) = 1;               /* Sectors per track */
  *((short *)sector+13) = 1;               /* Sides */
  *((short *)sector+14) = 0;               /* Hidden sectors */
  *(short *)((char *)sector+31) = 0xFEEB;  /* Boot code (JMP $) */
  write_sector(1, 0, sector);   /* Write boot sector */

  for (i = 0; i < IOCTL_msg.FATs; i++) {
    unsigned short sector_n =
        IOCTL_msg.reserved_sectors + IOCTL_msg.FAT_sectors * i;
    /* Write 1st FAT sector */
    memset(sector, 0, IOCTL_msg.bytes_per_sector);  /* Make 1st FAT sector */
    ((long *)sector)[0] = 0xFFFF00L | IOCTL_msg.media;
    write_sector(1, sector_n++, sector);

    /* Write FAT sectors from 2nd to last */
    ((long *)sector)[0] = 0L;
    for (Fsec = 1; Fsec < IOCTL_msg.FAT_sectors; Fsec++)
        write_sector(1, sector_n++, sector);
  }

  /* Write 1st directory sector */
  dir_start = IOCTL_msg.reserved_sectors + FAT_sectors;
  memcpy(sector, IOCTL_msg.volume, 11);
  ((char*)sector)[11] = FA_LABEL;
  write_sector(1, dir_start, sector);

  /* Write directory sectors from 2nd to last */
  memset(sector, 0, 12);
  for (Fsec = 1; Fsec < dir_sectors; Fsec++)
      write_sector(1, dir_start+Fsec, sector);

  free(sector);

  printf("Disk formatted\n\n");
}

/*
**  MAIN FUNCTION
*/

int main(int argc, char *argv[])
{
  printf("Sizeable RAMDisk Formatter, version "VERSION", "
         "PUBLIC DOMAIN, 1991\n\n");

  parse_cmdline(argc, argv);

  if (!drive)             /* If no drive specified */
    drive = getdisk()+1;  /* operate on current drive */

  init_drive();

  if (format_f) format_disk();
  else
    if (write_f != same) set_write_protect();

  return 0;
}
[ RETURN TO DIRECTORY ]