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