kernel/sys/sys.c

2068 lines
62 KiB
C

/***************************************************************
sys.c
DOS-C
sys utility for DOS-C
Copyright (c) 1991
Pasquale J. Villani
All Rights Reserved
This file is part of DOS-C.
DOS-C is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2, or (at your option) any later version.
DOS-C is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
DOS-C; see the file COPYING. If not, write to the Free Software Foundation,
675 Mass Ave, Cambridge, MA 02139, USA.
***************************************************************/
/* #define DEBUG */ /* to display extra information */
/* #define DDEBUG */ /* to enable display of sector dumps */
/* #define WITHOEMCOMPATBS */ /* include support for OEM MS/PC DOS 3.??-6.x */
#define FDCONFIG /* include support to configure FD kernel */
#define SYS_VERSION "v3.6f"
#define SYS_NAME "DOS System Installer "
#include <stdlib.h>
#ifndef __GNUC__
#include <dos.h>
#endif
#include <ctype.h>
#ifndef __GNUC__
#include <fcntl.h>
#include <sys/stat.h>
#ifdef __TURBOC__
#include <mem.h>
#else
#include <memory.h>
#endif
#endif
#include <string.h>
#ifdef __TURBOC__
#include <dir.h>
#endif
#define SYS_MAXPATH 260
#include "portab.h"
#include "algnbyte.h"
#include "device.h"
#include "dcb.h"
#include "xstructs.h"
#include "ddate.h"
#include "dtime.h"
#include "fat.h"
/* These definitions deliberately put here instead of
* #including <stdio.h> to make executable MUCH smaller
* using [s]printf from prf.c!
*/
extern int VA_CDECL printf(CONST char * fmt, ...);
extern int VA_CDECL sprintf(char * buff, CONST char * fmt, ...);
#include "fat12com.h"
#include "fat16com.h"
#ifdef WITHFAT32
#include "fat32chs.h"
#include "fat32lba.h"
#endif
#ifdef WITHOEMCOMPATBS
#include "oemfat12.h"
#include "oemfat16.h"
#endif
#ifndef __WATCOMC__
#ifdef __GNUC__
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#define O_BINARY 0
#define stricmp strcasecmp
#define memicmp strncasecmp
union REGS {
struct {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
} h;
struct {
unsigned short ax, bx, cx, dx, si, di, cflag;
} x;
};
struct SREGS {
unsigned short ds, es;
};
struct _diskfree_t {
unsigned short avail_clusters, sectors_per_cluster, bytes_per_sector;
};
int int86(int ivec, union REGS *in, union REGS *out)
{
/* must save sp for int25/26 */
asm("mov %5, (1f+1); jmp 0f; 0:push %%ds; mov %%di, %%dx; mov %%sp, %%di;"
"push %%di; push %%di;"
/* push twice to work both for int 25h/26h and int 21h */
"1:int $0x00; pop %%di; pop %%di;"
/* second pop always reads the correct SP value.
the first pop may read the FL left on stack. */
"mov %%di, %%sp; pop %%ds; sbb %0, %0" :
"=r"(out->x.cflag),
"=a"(out->x.ax), "=b"(out->x.bx), "=c"(out->x.cx), "=d"(out->x.dx) :
"q"((unsigned char)ivec), "a"(in->x.ax), "b"(in->x.bx),
"c"(in->x.cx), "D"(in->x.dx), "S"(in->x.si) :
"cc", "memory");
return out->x.ax;
}
int intdos(union REGS *in, union REGS *out)
{
return int86(0x21, in, out);
}
int intdosx(union REGS *in, union REGS *out, struct SREGS *s)
{
asm("push %%ds; mov %%bx, %%ds; int $0x21; pop %%ds; sbb %0, %0":
"=r"(out->x.cflag), "=a"(out->x.ax) :
"a"(in->x.ax), "c"(in->x.cx), "d"(in->x.dx),
"D"(in->x.di), "S"(in->x.si), "b"(s->ds), "e"(s->es) :
"cc", "memory");
return out->x.ax;
}
unsigned _dos_allocmem(unsigned size, unsigned *seg)
{
union REGS in, out;
in.h.ah = 0x48;
in.x.bx = size;
unsigned ret = intdos(&in, &out);
if (!out.x.cflag)
{
*seg = ret;
ret = 0;
}
return ret;
}
unsigned _dos_freemem(unsigned seg)
{
union REGS in, out;
struct SREGS s;
in.h.ah = 0x49;
s.es = seg;
return intdosx(&in, &out, &s);
}
unsigned int _dos_getdiskfree(unsigned int drive,
struct _diskfree_t *diskspace)
{
union REGS in, out;
in.x.ax = 0x3600;
in.x.dx = drive;
unsigned ret = intdos(&in, &out);
diskspace->avail_clusters = out.x.bx;
diskspace->sectors_per_cluster = out.x.dx;
diskspace->bytes_per_sector = out.x.cx;
return ret;
}
long filelength(int fhandle)
{
long ret = lseek(fhandle, 0, SEEK_END);
lseek(fhandle, 0, SEEK_SET);
return ret;
}
struct find_t {
char reserved[21];
unsigned char attrib;
unsigned short wr_time;
unsigned short wr_date;
unsigned long size;
char filename[13];
};
#define _A_NORMAL 0x00
#define _A_HIDDEN 0x02
#define _A_SYSTEM 0x04
int _dos_findfirst(const char *file_name, unsigned int attr,
struct find_t *find_tbuf)
{
union REGS in, out;
in.h.ah = 0x4e;
in.x.dx = FP_OFF(file_name);
in.x.cx = attr;
intdos(&in, &out);
if (out.x.cflag)
return out.x.ax;
memcpy(find_tbuf, (void *)0x80, sizeof(*find_tbuf));
return 0;
}
#else
#include <io.h>
#endif
/* returns current DOS drive, A=0, B=1,C=2, ... */
#ifdef __TURBOC__
#define getcurdrive (unsigned)getdisk
#else
unsigned getcurdrive(void)
{
union REGS regs;
regs.h.ah = 0x19;
int86(0x21, &regs, &regs);
return regs.h.al;
}
#endif
#else
/* returns current DOS drive, A=0, B=1,C=2, ... */
unsigned getcurdrive(void);
#pragma aux getcurdrive = \
"mov ah, 0x19" \
"int 0x21" \
"xor ah, ah" \
__value [__ax];
long filelength(int __handle);
#pragma aux filelength = \
"mov ax, 0x4202" \
"xor cx, cx" \
"xor dx, dx" \
"int 0x21" \
"push ax" \
"push dx" \
"mov ax, 0x4200" \
"xor cx, cx" \
"xor dx, dx" \
"int 0x21" \
"pop dx" \
"pop ax" \
__parm [__bx] \
__modify [__cx] \
__value [__dx __ax];
extern int unlink(const char *pathname);
/* some non-conforming functions to make the executable smaller */
int open(const char *pathname, int flags, ...)
{
int handle;
int result = (flags & O_CREAT ?
_dos_creat(pathname, _A_NORMAL, &handle) :
_dos_open(pathname, flags & (O_RDONLY | O_WRONLY | O_RDWR),
&handle));
return (result == 0 ? handle : -1);
}
int read(int fd, void *buf, unsigned count)
{
unsigned bytes;
int result = _dos_read(fd, buf, count, &bytes);
return (result == 0 ? bytes : -1);
}
int write(int fd, const void *buf, unsigned count)
{
unsigned bytes;
int result = _dos_write(fd, buf, count, &bytes);
return (result == 0 ? bytes : -1);
}
#define close _dos_close
#endif
#if defined(__WATCOMC__) || defined(__GNUC__)
int stat(const char *file_name, struct stat *statbuf)
{
struct find_t find_tbuf;
int ret = _dos_findfirst(file_name, _A_NORMAL | _A_HIDDEN | _A_SYSTEM, &find_tbuf);
statbuf->st_size = (off_t)find_tbuf.size;
/* statbuf->st_attr = (ULONG)find_tbuf.attrib; */
return ret;
}
#endif
#ifdef __WATCOMC__
/* WATCOM's getenv is case-insensitive which wastes a lot of space
for our purposes. So here's a simple case-sensitive one */
char *getenv(const char *name)
{
char **envp, *ep;
const char *np;
char ec, nc;
for (envp = environ; (ep = *envp) != NULL; envp++) {
np = name;
do {
ec = *ep++;
nc = *np++;
if (nc == 0) {
if (ec == '=')
return ep;
break;
}
} while (ec == nc);
}
return NULL;
}
#endif
BYTE pgm[] = "SYS";
#define SEC_SIZE 512
#define COPY_SIZE 0x4000
struct bootsectortype {
UBYTE bsJump[3];
char OemName[8];
UWORD bsBytesPerSec;
UBYTE bsSecPerClust;
UWORD bsResSectors;
UBYTE bsFATs;
UWORD bsRootDirEnts;
UWORD bsSectors;
UBYTE bsMedia;
UWORD bsFATsecs;
UWORD bsSecPerTrack;
UWORD bsHeads;
ULONG bsHiddenSecs;
ULONG bsHugeSectors;
UBYTE bsDriveNumber;
UBYTE bsReserved1;
UBYTE bsBootSignature;
ULONG bsVolumeID;
char bsVolumeLabel[11];
char bsFileSysType[8];
};
struct bootsectortype32 {
UBYTE bsJump[3];
char OemName[8];
UWORD bsBytesPerSec;
UBYTE bsSecPerClust;
UWORD bsResSectors;
UBYTE bsFATs;
UWORD bsRootDirEnts;
UWORD bsSectors;
UBYTE bsMedia;
UWORD bsFATsecs;
UWORD bsSecPerTrack;
UWORD bsHeads;
ULONG bsHiddenSecs;
ULONG bsHugeSectors;
ULONG bsBigFatSize;
UBYTE bsFlags;
UBYTE bsMajorVersion;
UWORD bsMinorVersion;
ULONG bsRootCluster;
UWORD bsFSInfoSector;
UWORD bsBackupBoot;
ULONG bsReserved2[3];
UBYTE bsDriveNumber;
UBYTE bsReserved3;
UBYTE bsExtendedSignature;
ULONG bsSerialNumber;
char bsVolumeLabel[11];
char bsFileSystemID[8];
};
/*
* globals needed by put_boot & check_space
*/
enum {FAT12 = 12, FAT16 = 16, FAT32 = 32} fs; /* file system type */
unsigned smallfat32;
/* static */ struct xfreespace x; /* we make this static to be 0 by default -
this avoids FAT misdetections */
#define SBOFFSET 11
#define SBSIZE (sizeof(struct bootsectortype) - SBOFFSET)
#define SBSIZE32 (sizeof(struct bootsectortype32) - SBOFFSET)
/* essentially - verify alignment on byte boundaries at compile time */
struct VerifyBootSectorSize {
char failure1[sizeof(struct bootsectortype) == 62 ? 1 : -1];
char failure2[sizeof(struct bootsectortype) == 62 ? 1 : 0];
/* (Watcom has a nice warning for this, by the way) */
};
#ifdef FDCONFIG
int FDKrnConfigMain(int argc, char **argv);
#endif
/* FreeDOS sys, we default to our kernel and load segment, but
if not found (or explicitly given) support OEM DOS variants
(such as DR-DOS or a FreeDOS kernel mimicing other DOSes).
Note: other (especially older) DOS versions expect the boot
loader to perform particular steps, which we may not do;
older PC/MS DOS variants may work with the OEM compatible
boot sector (optionally included).
*/
typedef struct DOSBootFiles {
const char * kernel; /* filename boot sector loads and chains to */
const char * dos; /* optional secondary file for OS */
WORD loadaddr; /* segment kernel file expects to start at for stdbs */
/* or offset to jump into kernel for oem compat bs */
BOOL stdbs; /* use FD boot sector (T) or oem compat one (F) */
LONG minsize; /* smallest dos file can be and be valid, 0=existance optional */
} DOSBootFiles;
#define FREEDOS_FILES { "KERNEL.SYS", NULL, 0x60/*:0*/, 1, 0 },
DOSBootFiles bootFiles[] = {
/* Note: This order is the order OEM:AUTO uses to determine DOS flavor. */
/* FreeDOS */ FREEDOS_FILES
/* EDR-DOS */ { "DRBIO.SYS", "DRDOS.SYS", 0x70, 1, 1 },
/* EDR-DOS */ { "EDRPACK.SYS", NULL, 0x60, 1, 0 },
/* EDR-DOS */ { "EDRDOS.COM", NULL, 0x60, 1, 0 },
/* OSS MS-DOS */ { "LMSPACK.SYS", NULL, 0x60, 1, 0 },
/* OSS MS-DOS */ { "LMSDOS.COM", NULL, 0x60, 1, 0 },
/* OpenDOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x70, 1, 1 },
#ifdef WITHOEMCOMPATBS
/* PC-DOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x0, 0, 6138 },
/* MS-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0, 0, 10240 },
/* W9x-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0200, 0, 0 },
/* Rx-DOS */ { "RXDOSBIO.SYS", "RXDOS.SYS", /*0x70:*/0x0, 0, 1 },
#endif
};
#define DOSFLAVORS (sizeof(bootFiles) / sizeof(*bootFiles))
/* associate friendly name with index into bootFiles array */
#define OEM_AUTO (-1) /* attempt to guess DOS on source drive */
#define OEM_FD 0 /* standard FreeDOS mode */
#define OEM_EDR 1 /* DRBIO, DRDOS version of EDR kernel */
#define OEM_LEDRPACK 2 /* lDOS drload version of EDR kernel */
#define OEM_LEDR 3 /* lDOS iniload version of EDR kernel */
#define OEM_LMSPACK 4 /* lDOS drload version of MS-DOS kernel */
#define OEM_LMS 5 /* lDOS iniload version of MS-DOS kernel */
#define OEM_OPENDOS 6 /* old IBMBIO, IBMDOS compatible DR-DOS versions */
#ifdef WITHOEMCOMPATBS
#define OEM_PC 7 /* use PC-DOS compatible boot sector and names */
#define OEM_MS 8 /* use PC-DOS compatible BS with MS names */
#define OEM_W9x 9 /* use PC-DOS compatible BS with MS names */
#define OEM_RX 10 /* use PC-DOS compatible BS with Rx names */
#endif
CONST char * msgDOS[DOSFLAVORS] = { /* order should match above items */
"\n", /* In standard FreeDOS mode, don't print anything special */
"Enhanced DR-DOS 7.01.07+ mode (DRBIO.SYS and DRDOS.SYS)\n",
"Enhanced DR-DOS mode (EDRPACK.SYS, lDOS drload)\n",
"Enhanced DR-DOS mode (EDRDOS.COM, lDOS iniload)\n",
"OSS MS-DOS mode (LMSPACK.SYS, lDOS drload)\n",
"OSS MS-DOS mode (LMSDOS.COM, lDOS iniload)\n",
"Novell DOS 7, Caldera OpenDOS, DR-DOS 7.02 - 7.03, 7.01.01 - 7.01.06 mode\n",
#ifdef WITHOEMCOMPATBS
"PC-DOS, DR DOS 5 - Novell DOS 7 mode\n",
"MS-DOS mode\n",
"Win9x DOS mode\n",
"RxDOS mode\n",
#endif
};
typedef struct SYSOptions {
BYTE srcDrive[SYS_MAXPATH]; /* source drive:[path], root assumed if no path */
BYTE dstDrive; /* destination drive [STD SYS option] */
int flavor; /* DOS variant we want to boot, default is AUTO/FD */
DOSBootFiles kernel; /* file name(s) and relevant data for kernel */
BYTE defBootDrive; /* value stored in boot sector for drive, eg 0x0=A, 0x80=C */
BOOL ignoreBIOS; /* true to NOP out boot sector code to get drive# from BIOS */
BOOL skipBakBSCopy; /* true to not copy boot sector to backup boot sector */
BOOL copyKernel; /* true to copy kernel files */
BOOL copyShell; /* true to copy command interpreter */
BOOL writeBS; /* true to write boot sector to drive/partition LBA 0 */
BYTE *bsFile; /* file name & path to save bs to when saving to file */
BYTE *bsFileOrig; /* file name & path to save original bs when backing up */
BYTE *fnKernel; /* optional override to source kernel filename (src only) */
BYTE *fnCmd; /* optional override to cmd interpreter filename (src & dest) */
enum {AUTO=0,LBA,CHS} force; /* optional force boot sector to only use LBA or CHS */
BOOL verbose; /* show extra (DEBUG) output */
} SYSOptions;
void dumpBS(const char *, int);
void restoreBS(const char *, int);
void put_boot(SYSOptions *opts);
BOOL check_space(COUNT, ULONG);
BOOL copy(SYSOptions *opts, const BYTE *source, COUNT drive, const BYTE * filename);
void showHelpAndExit(void)
{
printf(
"Usage: %s [source] drive: [bootsect] [{option}]\n"
" source = A:,B:,C:\\DOS\\,etc., or current directory if not given\n"
" drive = A,B,etc.\n"
" bootsect = name of 512-byte boot sector file image for drive:\n"
" to write to *instead* of real boot sector\n"
" {option} is one or more of the following:\n"
" /BOTH : write to *both* the real boot sector and the image file\n"
" /BOOTONLY: do *not* copy kernel / shell, only update boot sector or image\n"
" /UPDATE : copy kernel and update boot sector (do *not* copy shell)\n"
" /OEM : indicates boot sector, filenames, and load segment to use\n"
" /OEM:FD FreeDOS settings\n"
" /OEM:EDR Enhanced DR-DOS (DRBIO.SYS and DRDOS.SYS)\n"
" /OEM:LEDRPACK Enhanced DR-DOS (EDRPACK.SYS, lDOS drload)\n"
" /OEM:LEDR Enhanced DR-DOS (EDRDOS.COM, lDOS iniload)\n"
" /OEM:LMSPACK OSS MS-DOS (LMSPACK.SYS, lDOS drload)\n"
" /OEM:LMS OSS MS-DOS (LMSDOS.COM, lDOS iniload)\n"
" /OEM:OPENDOS Caldera OpenDOS 7.01 (and Novell DOS 7),\n"
" DR-DOS 7.02 - 7.03, 7.01.01 - 7.01.06\n"
#ifdef WITHOEMCOMPATBS
" /OEM:PC PC-DOS, DR DOS 5 - Novell DOS 7\n"
" /OEM:MS MS-DOS\n"
" /OEM:W9x MS Win9x DOS\n"
#endif
" default is /OEM[:AUTO], select DOS based on existing files\n"
" /K name : name of kernel to use in boot sector instead of %s\n"
" /L segm : hex load segment to use in boot sector instead of %02x\n"
" /B btdrv : hex BIOS # of boot drive set in bs, 0=A:, 80=1st hd,...\n"
" /FORCE : override automatic selection of BIOS related settings\n"
" /FORCE:BSDRV use boot drive # set in bootsector\n"
" /FORCE:BIOSDRV use boot drive # provided by BIOS\n"
" /NOBAKBS : skips copying boot sector to backup bs, FAT32 only else ignored\n"
#ifdef FDCONFIG
"%s CONFIG /help\n"
#endif
/*SYS, KERNEL.SYS/DRBIO.SYS 0x60/0x70*/
, pgm, bootFiles[0].kernel, bootFiles[0].loadaddr
#ifdef FDCONFIG
, pgm
#endif
);
exit(1);
}
/* get and validate arguments */
void initOptions(int argc, char *argv[], SYSOptions *opts)
{
int argno;
int drivearg = 0; /* drive argument, position of 1st or 2nd non option */
int srcarg = 0; /* nonzero if optional source argument */
BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */
struct stat fstatbuf;
/* initialize to defaults */
memset(opts, 0, sizeof(SYSOptions));
/* set srcDrive and dstDrive after processing args */
opts->flavor = OEM_AUTO; /* attempt to detect DOS user wants to boot */
opts->copyKernel = 1; /* actually copy the kernel and cmd interpreter to dstDrive */
opts->copyShell = 1;
/* cycle through processing cmd line arguments */
for(argno = 1; argno < argc; argno++)
{
char *argp = argv[argno];
if (argp[0] == '/') /* optional switch */
{
argp++; /* skip past the '/' character */
/* explicit request for base help/usage */
if ((*argp == '?') || (memicmp(argp, "HELP", 4) == 0))
{
showHelpAndExit();
}
/* enable extra (DEBUG) output */
else if (memicmp(argp, "VERBOSE", 7) == 0)
{
opts->verbose = 1;
}
/* write to *both* the real boot sector and the image file */
else if (memicmp(argp, "BOTH", 4) == 0)
{
opts->writeBS = 1; /* note: if bs file omitted, then same as omitting /BOTH */
}
/* do *not* copy kernel / shell, only update boot sector or image */
else if (memicmp(argp, "BOOTONLY", 8) == 0)
{
opts->copyKernel = 0;
opts->copyShell = 0;
}
/* copy kernel and update boot sector (do *not* copy shell) */
else if (memicmp(argp, "UPDATE", 6) == 0)
{
opts->copyKernel = 1;
opts->copyShell = 0;
}
/* indicates compatibility mode, fs, filenames, and load segment to use */
else if (memicmp(argp, "OEM", 3) == 0)
{
argp += 3;
if (!*argp)
opts->flavor = OEM_AUTO;
else if (*argp == ':')
{
argp++; /* point to DR/PC/MS that follows */
if (memicmp(argp, "AUTO", 4) == 0)
opts->flavor = OEM_AUTO;
else if (memicmp(argp, "LEDRPACK", 8) == 0)
opts->flavor = OEM_LEDRPACK;
else if (memicmp(argp, "LEDR", 4) == 0)
opts->flavor = OEM_LEDR;
else if (memicmp(argp, "EDR", 3) == 0)
opts->flavor = OEM_EDR;
else if (memicmp(argp, "LMSPACK", 7) == 0)
opts->flavor = OEM_LMSPACK;
else if (memicmp(argp, "LMS", 3) == 0)
opts->flavor = OEM_LMS;
else if (memicmp(argp, "OPENDOS", 7) == 0)
opts->flavor = OEM_OPENDOS;
#ifdef WITHOEMCOMPATBS
else if (memicmp(argp, "PC", 2) == 0)
opts->flavor = OEM_PC;
else if (memicmp(argp, "MS", 2) == 0)
opts->flavor = OEM_MS;
else if (memicmp(argp, "W9", 2) == 0)
opts->flavor = OEM_W9x;
else if (memicmp(argp, "RX", 2) == 0)
opts->flavor = OEM_RX;
#endif
else if (memicmp(argp, "FD", 2) == 0)
opts->flavor = OEM_FD;
else
{
printf("%s: unknown OEM qualifier %s\n", pgm, argp);
showHelpAndExit();
}
}
else
{
printf("%s: unknown OEM qualifier %s\n", pgm, argp);
showHelpAndExit();
}
}
/* override auto options */
else if (memicmp(argp, "FORCE", 5) == 0)
{
argp += 5;
if (*argp == ':')
{
argp++; /* point to CHS/LBA/... that follows */
/* specify which BIOS access mode to use */
if (memicmp(argp, "AUTO", 4) == 0) /* default */
opts->force = AUTO;
else if (memicmp(argp, "CHS", 3) == 0)
opts->force = CHS;
else if (memicmp(argp, "LBA", 3) == 0)
opts->force = LBA;
/* specify if BIOS or BOOTSECTOR provided boot drive # is to be used */
else if (memicmp(argp, "BSDRV", 5) == 0) /* same as FORCEDRV */
opts->ignoreBIOS = 1;
else if (memicmp(argp, "BIOSDRV", 7) == 0) /* always use BIOS passed */
opts->ignoreBIOS = -1;
else
{
printf("%s: invalid FORCE qualifier %s\n", pgm, argp);
showHelpAndExit();
}
}
else if (memicmp(argp, "DRV", 3) == 0) /* FORCEDRV */
{
/* force use of drive # set in bs instead of BIOS boot value */
/* deprecated, use FORCE:BSDRV */
opts->ignoreBIOS = 1;
}
else
{
printf("%s: invalid FORCE qualifier %s\n", pgm, argp);
showHelpAndExit();
}
}
/* skips copying boot sector to backup bs, FAT32 only else ignored */
else if (memicmp(argp, "NOBAKBS", 7) == 0)
{
opts->skipBakBSCopy = 1;
}
else if (argno + 1 < argc) /* two part options, /SWITCH VALUE */
{
argno++;
if (toupper(*argp) == 'K') /* set Kernel name */
{
opts->kernel.kernel = argv[argno];
}
else if (toupper(*argp) == 'L') /* set Load segment */
{
opts->kernel.loadaddr = (WORD)strtol(argv[argno], NULL, 16);
}
else if (memicmp(argp, "B", 2) == 0) /* set boot drive # */
{
opts->defBootDrive = (BYTE)strtol(argv[argno], NULL, 16);
}
/* options not documented by showHelpAndExit() */
else if (memicmp(argp, "SKFN", 4) == 0) /* set KERNEL.SYS input file and /OEM:FD */
{
opts->flavor = OEM_FD;
opts->fnKernel = argv[argno];
}
else if (memicmp(argp, "SCFN", 4) == 0) /* sets COMMAND.COM input file */
{
opts->fnCmd = argv[argno];
}
else if (memicmp(argp, "BACKUPBS", 8) == 0) /* save current bs before overwriting */
{
opts->bsFileOrig = argv[argno];
}
else if (memicmp(argp, "DUMPBS", 6) == 0) /* save current bs and exit */
{
if (drivearg)
dumpBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A'));
else
printf("%s: unspecified drive, unable to obtain boot sector\n", pgm);
exit(1);
}
else if (memicmp(argp, "RESTORBS", 8) == 0) /* overwrite bs and exit */
{
if (drivearg)
restoreBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A'));
else
printf("%s: unspecified drive, unable to restore boot sector\n", pgm);
exit(1);
}
else
{
printf("%s: unknown option, %s\n", pgm, argv[argno]);
showHelpAndExit();
}
}
else
{
printf("%s: unknown option or missing parameter, %s\n", pgm, argv[argno]);
showHelpAndExit();
}
}
else if (!drivearg)
{
drivearg = argno; /* either source or destination drive */
}
else if (!srcarg /* && drivearg */ && !opts->bsFile)
{
/* need to determine is user specified [source] dest or dest [bootfile] (or [source] dest [bootfile])
- dest must be either X or X: as only a drive specifier without any path is valid -
if 1st arg not drive and 2nd is then [source] dest form
if 1st arg drive and 2nd is not drive then dest [bootfile] form
if both 1st arg and 2nd are not drives then invalid arguments
if both 1st arg and 2nd are drives then assume [source] dest form (use ./X form is single letter used)
*/
if (!argv[drivearg][1] || (argv[drivearg][1]==':' && !argv[drivearg][2])) /* if 1st arg drive */
{
if (!argv[argno][1] || (argv[argno][1]==':' && !argv[argno][2])) /* if 2nd arg drive */
{
srcarg = drivearg; /* set source path */
drivearg = argno; /* set destination drive */
}
else
{
opts->bsFile = argv[argno];
}
}
else
{
if (!argv[argno][1] || (argv[argno][1]==':' && !argv[argno][2])) /* if 2nd arg drive */
{
srcarg = drivearg; /* set source path */
drivearg = argno; /* set destination drive */
}
else
{
goto EXITBADARG;
}
}
}
else if (!opts->bsFile /* && srcarg && drivearg */)
{
opts->bsFile = argv[argno];
}
else /* if (opts->bsFile && srcarg && drivearg) */
{
EXITBADARG:
printf("%s: invalid argument %s\n", pgm, argv[argno]);
showHelpAndExit();
}
}
/* if neither BOTH nor a boot sector file specified, then write to boot record */
if (!opts->bsFile)
opts->writeBS = 1;
/* set dest path */
if (!drivearg)
showHelpAndExit();
opts->dstDrive = (BYTE)(toupper(*(argv[drivearg])) - 'A');
if (/* (opts->dstDrive < 0) || */ (opts->dstDrive >= 26))
{
printf("%s: drive %c must be A:..Z:\n", pgm, *(argv[drivearg]));
exit(1);
}
/* set source path, default to current drive */
sprintf(opts->srcDrive, "%c:", 'A' + getcurdrive());
if (srcarg)
{
int slen;
/* set source path, reserving room to append filename */
if ( (argv[srcarg][1] == ':') || ((argv[srcarg][0]=='\\') && (argv[srcarg][1] == '\\')) )
strncpy(opts->srcDrive, argv[srcarg], SYS_MAXPATH-13);
else if (argv[srcarg][1] == '\0') /* assume 1 char is drive not path specifier */
sprintf(opts->srcDrive, "%c:", toupper(*(argv[srcarg])));
else /* only path provided, append to default drive */
strncat(opts->srcDrive, argv[srcarg], SYS_MAXPATH-15);
slen = strlen(opts->srcDrive);
/* if path follows drive, ensure ends in a slash, ie X:-->X: or X:.\mypath-->X:.\mypath\ */
if ((slen>2) && (opts->srcDrive[slen-1] != '\\') && (opts->srcDrive[slen-1] != '/'))
strcat(opts->srcDrive, "\\");
}
/* source path is now in form of just a drive, "X:"
or form of drive + path + directory separator, "X:\path\" or "\\path\"
If just drive we try current path then root, else just indicated path.
*/
/* if source and dest are same drive, then source should not be root,
so if is same drive and not explicit path, force only current
Note: actual copy routine prevents overwriting self when src=dst
*/
if ( (opts->dstDrive == (toupper(*(opts->srcDrive))-'A')) && (!opts->srcDrive[2]) )
strcat(opts->srcDrive, ".\\");
/* attempt to detect compatibility settings user needs */
if (opts->flavor == OEM_AUTO)
{
/* 1st loop checking current just source path provided */
for (argno = 0; argno < DOSFLAVORS; argno++)
{
/* look for existing file matching kernel filename */
sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].kernel);
if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */
if (!fstatbuf.st_size) continue; /* file must not be empty */
/* now check if secondary file exists and of minimal size */
if (bootFiles[argno].minsize)
{
sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].dos);
if (stat(srcFile, &fstatbuf)) continue;
if (fstatbuf.st_size < bootFiles[argno].minsize) continue;
}
/* above criteria succeeded, so default to corresponding DOS */
opts->flavor = argno;
break;
}
/* if no match, and source just drive, try root */
if ( (opts->flavor == OEM_AUTO) && (!opts->srcDrive[2]) )
{
for (argno = 0; argno < DOSFLAVORS; argno++)
{
/* look for existing file matching kernel filename */
sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].kernel);
if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */
if (!fstatbuf.st_size) continue; /* file must not be empty */
/* now check if secondary file exists and of minimal size */
if (bootFiles[argno].minsize)
{
sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].dos);
if (stat(srcFile, &fstatbuf)) continue;
if (fstatbuf.st_size < bootFiles[argno].minsize) continue;
}
/* above criteria succeeded, so default to corresponding DOS */
opts->flavor = argno;
strcat(opts->srcDrive, "\\"); /* indicate to use root from now on */
break;
}
}
}
/* if unable to determine DOS, assume FreeDOS */
if (opts->flavor == OEM_AUTO) opts->flavor = OEM_FD;
/* set compatibility settings not explicitly set */
if (!opts->kernel.kernel) opts->kernel.kernel = bootFiles[opts->flavor].kernel;
if (!opts->kernel.dos) opts->kernel.dos = bootFiles[opts->flavor].dos;
if (!opts->kernel.loadaddr) opts->kernel.loadaddr = bootFiles[opts->flavor].loadaddr;
opts->kernel.stdbs = bootFiles[opts->flavor].stdbs;
opts->kernel.minsize = bootFiles[opts->flavor].minsize;
/* did user insist on always using BIOS provided drive # */
if (opts->ignoreBIOS == -1)
opts->ignoreBIOS = 0; /* its really a boolean value in rest of code */
/* if destination is floppy (A: or B:) then use drive # stored in boot sector */
else if (opts->dstDrive < 2)
opts->ignoreBIOS = 1;
/* if bios drive to store in boot sector not set and not floppy set to 1st hd */
if (!opts->defBootDrive && (opts->dstDrive >= 2))
opts->defBootDrive = 0x80;
/* else opts->defBootDrive = 0x0; the 1st floppy */
/* unless we are only setting boot sector, verify kernel file exists */
if (opts->copyKernel)
{
/* check kernel (primary file) 1st */
sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
if (stat(srcFile, &fstatbuf)) /* if !exists() */
{
/* check root path as well if src is drive only */
sprintf(srcFile, "%s\\%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
if (opts->srcDrive[2] || stat(srcFile, &fstatbuf))
{
printf("%s: failed to find kernel file %s\n", pgm, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
exit(1);
}
/* else found, but in root, so force to always use root */
strcat(opts->srcDrive, "\\");
}
/* now check for secondary file */
if (opts->kernel.dos && opts->kernel.minsize)
{
sprintf(srcFile, "%s%s", opts->srcDrive, opts->kernel.dos);
if (stat(srcFile, &fstatbuf))
{
printf("%s: failed to find source file %s\n", pgm, opts->kernel.dos);
exit(1);
}
if (fstatbuf.st_size < opts->kernel.minsize)
{
printf("%s: source file %s appears corrupt, invalid size\n", pgm, opts->kernel.dos);
exit(1);
}
}
}
/* if updating or only setting bootsector then skip this check */
if (opts->copyShell)
{
/* lastly check for command interpreter */
sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnCmd)?opts->fnCmd:"COMMAND.COM");
if (stat(srcFile, &fstatbuf)) /* if !exists() */
{
char *comspec = getenv("COMSPEC");
if (opts->fnCmd || (comspec == NULL) || stat(comspec, &fstatbuf))
{
printf("%s: failed to find command interpreter (shell) file %s\n", pgm, srcFile);
exit(1);
}
}
}
}
int main(int argc, char **argv)
{
SYSOptions opts; /* boot options and other flags */
BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */
#ifdef FDCONFIG
if (argc > 1 && memicmp(argv[1], "CONFIG", 6) == 0)
{
exit(FDKrnConfigMain(argc, argv));
}
#endif
initOptions(argc, argv, &opts);
if (opts.verbose) {
printf(SYS_NAME SYS_VERSION ", " __DATE__ "\n");
printf(msgDOS[opts.flavor]);
}
if (opts.verbose)
printf("Processing boot sector...\n");
put_boot(&opts);
if (opts.copyKernel)
{
if (opts.verbose)
printf("Now copying system files...\n");
sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnKernel)?opts.fnKernel:opts.kernel.kernel);
if (!copy(&opts, srcFile, opts.dstDrive, opts.kernel.kernel))
{
printf("%s: cannot copy \"%s\"\n", pgm, srcFile);
exit(1);
} /* copy kernel */
if (opts.kernel.dos)
{
sprintf(srcFile, "%s%s", opts.srcDrive, opts.kernel.dos);
if (!copy(&opts, srcFile, opts.dstDrive, opts.kernel.dos) && opts.kernel.minsize)
{
printf("%s: cannot copy \"%s\"\n", pgm, srcFile);
exit(1);
} /* copy secondary file (DOS) */
}
}
if (opts.copyShell)
{
if (opts.verbose)
printf("Copying shell (command interpreter)...\n");
/* copy command.com, 1st try source path, then try %COMSPEC% */
sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM");
if (!copy(&opts, srcFile, opts.dstDrive, "COMMAND.COM"))
{
char *comspec = getenv("COMSPEC");
if (!opts.fnCmd && (comspec != NULL))
printf("%s: Trying shell from %%COMSPEC%%=\"%s\"\n", pgm, comspec);
if (opts.fnCmd || (comspec == NULL) || !copy(&opts, comspec, opts.dstDrive, "COMMAND.COM"))
{
printf("\n%s: failed to find command interpreter (shell) file %s\n", pgm, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM");
exit(1);
}
} /* copy shell */
}
if (opts.verbose)
printf("\n");
printf("System transferred.\n\n");
return 0;
}
#ifdef DDEBUG
VOID dump_sector(unsigned char far * sec)
{
COUNT x, y;
char c;
for (x = 0; x < 32; x++)
{
printf("%03X ", x * 16);
for (y = 0; y < 16; y++)
{
printf("%02X ", sec[x * 16 + y]);
}
for (y = 0; y < 16; y++)
{
c = sec[x * 16 + y];
if (isprint(c))
printf("%c", c);
else
printf(".");
}
printf("\n");
}
printf("\n");
}
#endif
#ifdef __WATCOMC__
int absread(int DosDrive, int nsects, int foo, void *diskReadPacket);
#pragma aux absread = \
"push bp" \
"int 0x25" \
"sbb ax, ax" \
"popf" \
"pop bp" \
__parm [__ax] [__cx] [__dx] [__bx] \
__modify [__si __di] \
__value [__ax];
int abswrite(int DosDrive, int nsects, int foo, void *diskReadPacket);
#pragma aux abswrite = \
"push bp" \
"int 0x26" \
"sbb ax, ax" \
"popf" \
"pop bp" \
__parm [__ax] [__cx] [__dx] [__bx] \
__modify [__si __di] \
__value [__ax];
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno);
#pragma aux fat32readwrite = \
"mov ax, 0x7305" \
"mov cx, 0xffff" \
"int 0x21" \
"sbb ax, ax" \
__parm [__dx] [__bx] [__si] \
__modify [__cx __dx __si] \
__value [__ax];
void reset_drive(int DosDrive);
#pragma aux reset_drive = \
"push ds" \
"inc dx" \
"mov ah, 0xd" \
"int 0x21" \
"mov ah,0x32" \
"int 0x21" \
"pop ds" \
__parm [__dx] \
__modify [__ax __bx];
void truename(char far *dest, const char *src);
#pragma aux truename = \
"mov ah,0x60" \
"int 0x21" \
__parm [__es __di] [__si];
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par);
#pragma aux generic_block_ioctl = \
"mov ax, 0x440d" \
"int 0x21" \
"sbb ax, ax" \
__value [__ax] \
__parm [__bx] [__cx] [__dx]; /* BH must be 0 for lock! */
#else
#ifndef __TURBOC__
int int2526readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
{
union REGS regs;
regs.h.al = (BYTE) DosDrive;
regs.x.bx = (short)diskReadPacket;
regs.x.cx = 0xffff;
int86(intno, &regs, &regs);
return regs.x.cflag;
}
#define absread(DosDrive, foo, cx, diskReadPacket) \
int2526readwrite(DosDrive, diskReadPacket, 0x25)
#define abswrite(DosDrive, foo, cx, diskReadPacket) \
int2526readwrite(DosDrive, diskReadPacket, 0x26)
#endif
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
{
union REGS regs;
regs.x.ax = 0x7305;
regs.h.dl = DosDrive;
regs.x.bx = (short)diskReadPacket;
regs.x.cx = 0xffff;
regs.x.si = intno;
intdos(&regs, &regs);
return regs.x.cflag;
} /* fat32readwrite */
void reset_drive(int DosDrive)
{
union REGS regs;
regs.h.ah = 0xd;
intdos(&regs, &regs);
regs.h.ah = 0x32;
regs.h.dl = DosDrive + 1;
intdos(&regs, &regs);
} /* reset_drive */
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par)
{
union REGS regs;
regs.x.ax = 0x440d;
regs.x.cx = cx;
regs.x.dx = (unsigned)par;
regs.x.bx = drive; /* BH must be 0 for lock! */
intdos(&regs, &regs);
return regs.x.cflag;
} /* generic_block_ioctl */
void truename(char *dest, const char *src)
{
union REGS regs;
struct SREGS sregs;
regs.h.ah = 0x60;
sregs.es = FP_SEG(dest);
regs.x.di = FP_OFF(dest);
sregs.ds = FP_SEG(src);
regs.x.si = FP_OFF(src);
intdosx(&regs, &regs, &sregs);
} /* truename */
#endif
int MyAbsReadWrite(int DosDrive, int count, ULONG sector, void *buffer,
int write)
{
struct {
unsigned long sectorNumber;
unsigned short count;
void far *address;
} diskReadPacket;
diskReadPacket.sectorNumber = sector;
diskReadPacket.count = count;
diskReadPacket.address = buffer;
if ((!write && absread(DosDrive, -1, -1, &diskReadPacket) == -1)
|| (write && abswrite(DosDrive, -1, -1, &diskReadPacket) == -1))
{
#ifdef WITHFAT32
return fat32readwrite(DosDrive + 1, &diskReadPacket, write);
#else
return 0xff;
#endif
}
return 0;
} /* MyAbsReadWrite */
#ifdef __WATCOMC__
unsigned getextdrivespace(void far *drivename, void *buf, unsigned buf_size);
#pragma aux getextdrivespace = \
"mov ax, 0x7303" \
"stc" \
"int 0x21" \
"sbb ax, ax" \
__parm [__es __dx] [__di] [__cx] \
__value [__ax];
#else /* !defined __WATCOMC__ */
unsigned getextdrivespace(void *drivename, void *buf, unsigned buf_size)
{
union REGS regs;
struct SREGS sregs;
regs.x.ax = 0x7303; /* get extended drive free space */
sregs.es = FP_SEG(buf);
regs.x.di = FP_OFF(buf);
sregs.ds = FP_SEG(drivename);
regs.x.dx = FP_OFF(drivename);
regs.x.cx = buf_size;
intdosx(&regs, &regs, &sregs);
return regs.x.ax == 0x7300 || regs.x.cflag;
} /* getextdrivespace */
#endif /* defined __WATCOMC__ */
#ifdef __WATCOMC__
/*
* If BIOS has got LBA extensions, after the Int 13h call BX will be 0xAA55.
* If extended disk access functions are supported, bit 0 of CX will be set.
*/
BOOL haveLBA(void); /* return TRUE if we have LBA BIOS, FALSE otherwise */
#pragma aux haveLBA = \
"mov ax, 0x4100" /* IBM/MS Int 13h Extensions - installation check */ \
"mov bx, 0x55AA" \
"mov dl, 0x80" \
"int 0x13" \
"xor ax, ax" \
"cmp bx, 0xAA55" \
"jne quit" \
"and cx, 1" \
"xchg cx, ax" \
"quit:" \
__modify [__bx __cx __dx] \
__value [__ax];
#else
BOOL haveLBA(void)
{
union REGS r;
r.x.ax = 0x4100;
r.x.bx = 0x55AA;
r.h.dl = 0x80;
int86(0x13, &r, &r);
return r.x.bx == 0xAA55 && r.x.cx & 1;
}
#endif
void correct_bpb(struct bootsectortype *default_bpb,
struct bootsectortype *oldboot, BOOL verbose)
{
/* don't touch partitions (floppies most likely) that don't have hidden
sectors */
if (default_bpb->bsHiddenSecs == 0)
return;
if (verbose)
{
printf("Old boot sector values: sectors/track: %u, heads: %u, hidden: %lu\n",
oldboot->bsSecPerTrack, oldboot->bsHeads, oldboot->bsHiddenSecs);
printf("Default and new boot sector values: sectors/track: %u, heads: %u, "
"hidden: %lu\n", default_bpb->bsSecPerTrack, default_bpb->bsHeads,
default_bpb->bsHiddenSecs);
}
oldboot->bsSecPerTrack = default_bpb->bsSecPerTrack;
oldboot->bsHeads = default_bpb->bsHeads;
oldboot->bsHiddenSecs = default_bpb->bsHiddenSecs;
}
/* reads in boot sector (1st SEC_SIZE bytes) from file */
void readBS(const char *bsFile, UBYTE *bootsector)
{
if (bsFile != NULL)
{
int fd;
#ifdef DEBUG
printf("reading bootsector from file %s\n", bsFile);
#endif
/* open boot sector file, it must exists, then overwrite
drive with 1st SEC_SIZE bytes from the [image] file
*/
if ((fd = open(bsFile, O_RDONLY | O_BINARY)) < 0)
{
printf("%s: can't open\"%s\"\nDOS errnum %d", pgm, bsFile, errno);
exit(1);
}
if (read(fd, bootsector, SEC_SIZE) != SEC_SIZE)
{
printf("%s: failed to read %u bytes from %s\n", pgm, SEC_SIZE, bsFile);
close(fd);
exit(1);
}
close(fd);
}
}
/* write bs in bsFile to drive's boot record unmodified */
void restoreBS(const char *bsFile, int drive)
{
UBYTE bootsector[SEC_SIZE];
if (bsFile == NULL)
{
printf("%s: missing filename of boot sector to restore\n", pgm);
exit(1);
}
readBS(bsFile, bootsector);
/* lock drive */
generic_block_ioctl(drive + 1, 0x84a, NULL);
reset_drive(drive);
/* write bootsector to drive */
if (MyAbsReadWrite(drive, 1, 0, bootsector, 1) != 0)
{
printf("%s: failed to write boot sector to drive %c:\n", pgm, drive + 'A');
exit(1);
}
reset_drive(drive);
/* unlock_drive */
generic_block_ioctl(drive + 1, 0x86a, NULL);
}
/* write bootsector to file bsFile */
void saveBS(const char *bsFile, UBYTE *bootsector)
{
if (bsFile != NULL)
{
int fd;
#ifdef DEBUG
printf("writing bootsector to file %s\n", bsFile);
#endif
/* open boot sector file, create it if not exists,
but don't truncate if exists so we can replace
1st SEC_SIZE bytes of an image file
*/
if ((fd = open(bsFile, O_WRONLY | O_CREAT | O_BINARY,
S_IREAD | S_IWRITE)) < 0)
{
printf("%s: can't create\"%s\"\nDOS errnum %d", pgm, bsFile, errno);
exit(1);
}
if (write(fd, bootsector, SEC_SIZE) != SEC_SIZE)
{
printf("%s: failed to write %u bytes to %s\n", pgm, SEC_SIZE, bsFile);
close(fd);
/* unlink(bsFile); don't delete in case was image */
exit(1);
}
close(fd);
} /* if write boot sector file */
}
/* write drive's boot record unmodified to bsFile */
void dumpBS(const char *bsFile, int drive)
{
UBYTE bootsector[SEC_SIZE];
if (bsFile == NULL)
{
printf("%s: missing filename to dump boot sector to\n", pgm);
exit(1);
}
/* lock drive */
generic_block_ioctl(drive + 1, 0x84a, NULL);
reset_drive(drive);
/* suggestion: allow reading from a boot sector or image file here */
if (MyAbsReadWrite(drive, 1, 0, bootsector, 0) != 0)
{
printf("%s: failed to read boot sector for drive %c:\n", pgm, drive + 'A');
exit(1);
}
reset_drive(drive);
/* unlock_drive */
generic_block_ioctl(drive + 1, 0x86a, NULL);
saveBS(bsFile, bootsector);
}
void put_boot(SYSOptions *opts)
{
struct bootsectortype32 *bs32;
struct bootsectortype *bs;
UBYTE oldboot[SEC_SIZE], newboot[SEC_SIZE];
UBYTE default_bpb[0x5c];
int bsBiosMovOff; /* offset in bs to mov [drive],dl that we NOP out */
if (opts->verbose)
{
printf("Reading old bootsector from drive %c:\n", opts->dstDrive + 'A');
}
/* lock drive */
generic_block_ioctl(opts->dstDrive + 1, 0x84a, NULL);
reset_drive(opts->dstDrive);
/* suggestion: allow reading from a boot sector or image file here */
if (MyAbsReadWrite(opts->dstDrive, 1, 0, oldboot, 0) != 0)
{
printf("%s: can't read old boot sector for drive %c:\n", pgm, opts->dstDrive + 'A');
exit(1);
}
#ifdef DDEBUG
printf("Old Boot Sector:\n");
dump_sector(oldboot);
#endif
/* backup original boot sector when requested */
if (opts->bsFileOrig)
{
printf("Backing up original boot sector to %s\n", opts->bsFileOrig);
saveBS(opts->bsFileOrig, oldboot);
}
bs = (struct bootsectortype *)oldboot;
if (bs->bsBytesPerSec != SEC_SIZE)
{
printf("Sector size is not 512 but %d bytes - not currently supported!\n",
bs->bsBytesPerSec);
exit(1); /* Japan?! */
}
{
/* see "FAT: General Overview of On-Disk Format" v1.02, 5.V.1999
* (http://www.nondot.org/sabre/os/files/FileSystems/FatFormat.pdf)
*/
ULONG fatSize, totalSectors, dataSectors, clusters;
UCOUNT rootDirSectors;
bs32 = (struct bootsectortype32 *)bs;
rootDirSectors = (bs->bsRootDirEnts * DIRENT_SIZE /* 32 */
+ bs32->bsBytesPerSec - 1) / bs32->bsBytesPerSec;
fatSize = bs32->bsFATsecs ? bs32->bsFATsecs : bs32->bsBigFatSize;
totalSectors = bs32->bsSectors ? bs32->bsSectors : bs32->bsHugeSectors;
dataSectors = totalSectors
- bs32->bsResSectors - (bs32->bsFATs * fatSize) - rootDirSectors;
clusters = dataSectors / (((bs32->bsSecPerClust - 1) & 0xFF) + 1);
if (bs32->bsFATsecs == 0) {
if (clusters >= 0xFFFfff5UL) { /* FAT32 has 28 significant bits */
printf("Too many clusters (%lXh) for FAT32 file system!\n", clusters);
exit(1);
}
fs = FAT32;
if (clusters < FAT_MAGIC16)
smallfat32 = 1;
} else {
if (clusters < FAT_MAGIC) /* < 4085 */
fs = FAT12;
else if (clusters < FAT_MAGIC16) /* < 65525 */
fs = FAT16;
else {
printf("Too many clusters (%lXh) for non-FAT32 file system!\n", clusters);
exit(1);
}
}
}
/* bit 0 set if function to use current BPB, clear if Device
BIOS Parameter Block field contains new default BPB
bit 1 set if function to use track layout fields only
must be clear if CL=60h
bit 2 set if all sectors in track same size (should be set) (RBIL) */
default_bpb[0] = 4;
if (fs == FAT32)
{
if (opts->verbose)
printf("FAT type: FAT32%s\n", smallfat32 ? " (small)" : "");
/* get default bpb (but not for floppies) */
if (opts->dstDrive >= 2 &&
generic_block_ioctl(opts->dstDrive + 1, 0x4860, default_bpb) == 0)
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs, opts->verbose);
#ifdef WITHFAT32 /* copy one of the FAT32 boot sectors */
if (!opts->kernel.stdbs) /* MS/PC DOS compatible BS requested */
{
printf("%s: FAT32 versions of PC/MS DOS compatible boot sectors\n"
"are not supported.\n", pgm);
exit(1);
}
/* user may force explicity lba or chs, otherwise base on if LBA available */
if ((opts->force==LBA) || ((opts->force==AUTO) && haveLBA()))
memcpy(newboot, fat32lba, SEC_SIZE);
else /* either auto mode & no LBA detected or forced CHS */
memcpy(newboot, fat32chs, SEC_SIZE);
#else
printf("SYS hasn't been compiled with FAT32 support.\n"
"Consider using -DWITHFAT32 option.\n");
exit(1);
#endif
}
else
{ /* copy the FAT12/16 CHS+LBA boot sector */
if (opts->verbose)
printf("FAT type: FAT1%c\n", fs + '0' - 10);
if (opts->dstDrive >= 2 &&
generic_block_ioctl(opts->dstDrive + 1, 0x860, default_bpb) == 0)
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs, opts->verbose);
if (opts->kernel.stdbs)
{
/* copy over appropriate boot sector, FAT12 or FAT16 */
memcpy(newboot, (fs == FAT16) ? fat16com : fat12com, SEC_SIZE);
/* !!! if boot sector changes then update these locations !!! */
{
/* magic offset: LBA detection */
unsigned offset;
offset = (fs == FAT16) ? 0x178 : 0x17B;
if ( (newboot[offset]==0x84) && (newboot[offset+1]==0xD2) ) /* test dl,dl */
{
/* if always use LBA then NOP out conditional jmp over LBA logic if A: */
if (opts->force==LBA)
{
offset+=2; /* jz */
newboot[offset] = 0x90; /* NOP */ ++offset;
newboot[offset] = 0x90; /* NOP */
}
else if (opts->force==CHS) /* if force CHS then always skip LBA logic */
{
newboot[offset] = 0x30; /* XOR */
}
}
else
{
printf("%s: Internal error: FAT1%c LBA detect unexpected content\n",
pgm, fs == FAT12 ? '2' : '6');
exit(1);
}
}
}
else
{
#ifdef WITHOEMCOMPATBS
printf("Using OEM (PC/MS-DOS) compatible boot sector.\n");
memcpy(newboot, (fs == FAT16) ? oemfat16 : oemfat12, SEC_SIZE);
#else
printf("Internal Error: no OEM compatible boot sector!\n");
#endif
}
}
/* Copy disk parameter from old sector to new sector */
#ifdef WITHFAT32
if (fs == FAT32)
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE32);
else
#endif
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE);
bs = (struct bootsectortype *)newboot;
/* originally OemName was "FreeDOS", changed for better compatibility */
memcpy(bs->OemName, "FRDOS5.1", 8); /* Win9x seems to require
5 uppercase letters,
digit(4 or 5) dot digit */
#ifdef WITHFAT32
if (fs == FAT32)
{
bs32 = (struct bootsectortype32 *)newboot;
/* ensure appears valid, if not then force valid */
if ((bs32->bsBackupBoot < 1) || (bs32->bsBackupBoot > bs32->bsResSectors))
{
if (opts->verbose)
printf("BPB appears to have invalid backup boot sector #, forcing to default.\n");
bs32->bsBackupBoot = 0x6; /* ensure set, 6 is MS defined bs size */
}
bs32->bsDriveNumber = opts->defBootDrive;
/* the location of the "0060" segment portion of the far pointer
in the boot sector is just before cont: in boot*.asm.
This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16
force use of value stored in bs by NOPping out mov [drive], dl
0x82: 88h,56h,40h for fat32 chs & lba boot sectors
i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!!
*/
if (opts->kernel.stdbs)
{
/* magic offset: loadsegoff_60 */
int defaultload = *(int *)(&newboot[0x78]);
if (defaultload != 0x60 && defaultload != 0x70) {
printf("%s: Internal error: FAT32 load seg unexpected content\n", pgm);
exit(1);
}
*(int *)(&newboot[0x78]) = opts->kernel.loadaddr;
bsBiosMovOff = 0x82; /* magic offset: mov byte [bp + 40h], dl */
}
else /* compatible bs */
{
printf("%s: INTERNAL ERROR: how did you get here?\n", pgm);
exit(1);
}
#ifdef DEBUG
printf(" FAT starts at sector %lx + %x\n",
bs32->bsHiddenSecs, bs32->bsResSectors);
#endif
}
else
#endif
{
/* establish default BIOS drive # set in boot sector */
bs->bsDriveNumber = opts->defBootDrive;
/* the location of the "0060" segment portion of the far pointer
in the boot sector is just before cont: in boot*.asm.
This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16
The oem boot sectors do not have/need this value for patching.
the location of the jmp address (patching from
EA00007000 [jmp 0x0070:0000] to EA00207000 [jmp 0x0070:0200])
0x11b: for fat12 oem boot sector
0x118: for fat16 oem boot sector
The standard boot sectors do not have/need this value patched.
force use of value stored in bs by NOPping out mov [drive], dl
0x66: 88h,56h,24h for fat16 and fat12 boot sectors
0x4F: 88h,56h,24h for oem compatible fat16 and fat12 boot sectors
i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!!
*/
if (opts->kernel.stdbs)
{
/* magic offset: loadsegoff_60 */
int defaultload = *(int *)(&newboot[0x5C]);
if (defaultload != 0x60 && defaultload != 0x70) {
printf("%s: Internal error: FAT1%c load seg unexpected content\n",
pgm, fs == FAT12 ? '2' : '6');
exit(1);
}
/* this sets the segment we load the kernel to, default is 0x60:0 */
*(int *)(&newboot[0x5C]) = opts->kernel.loadaddr;
bsBiosMovOff = 0x66; /* magic offset: mov byte [bp + 24h], dl */
}
else
{
int defaultload;
/* load segment hard coded to 0x70 in oem compatible boot sector, */
/* this however changes the offset jumped to default 0x70:0 */
if (fs == FAT12) {
/* magic offset: jmp LOADSEG:xxxxh */
defaultload = *(int *)(&newboot[0x11A]);
*(int *)(&newboot[0x11A]) = opts->kernel.loadaddr;
} else {
/* magic offset: jmp LOADSEG:xxxxh */
defaultload = *(int *)(&newboot[0x118]);
*(int *)(&newboot[0x118]) = opts->kernel.loadaddr;
}
if (defaultload != 0x0 && defaultload != 0x200) {
printf("%s: Internal error: OEM FAT1%c load ofs unexpected content\n",
pgm, fs == FAT12 ? '2' : '6');
exit(1);
}
bsBiosMovOff = 0x4F; /* magic offset: mov byte [bp + 24h], dl */
}
}
if ( (newboot[bsBiosMovOff]==0x88) && (newboot[bsBiosMovOff+1]==0x56) )
{
if (opts->ignoreBIOS)
{
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
}
}
else
{
printf("%s: Internal error: Unit save unexpected content\n", pgm);
exit(1);
}
if (opts->verbose) /* display information about filesystem */
{
printf("Root dir entries = %u\n", bs->bsRootDirEnts);
printf("FAT starts at sector (%lu + %u)\n",
bs->bsHiddenSecs, bs->bsResSectors);
printf("Root directory starts at sector (PREVIOUS + %u * %u)\n",
bs->bsFATsecs, bs->bsFATs);
}
{
int i = 0;
/* magic offset: (first) kernel filename */
memset(&newboot[0x1f1], ' ', 11);
while (opts->kernel.kernel[i] && opts->kernel.kernel[i] != '.')
{
if (i < 8)
newboot[0x1f1+i] = toupper(opts->kernel.kernel[i]);
i++;
}
if (opts->kernel.kernel[i] == '.')
{
/* copy extension */
int j = 0;
i++;
while (opts->kernel.kernel[i+j] && j < 3)
{
newboot[0x1f9+j] = toupper(opts->kernel.kernel[i+j]);
j++;
}
}
}
if (opts->verbose)
{
/* there's a zero past the kernel name in all boot sectors */
printf("Boot sector kernel name set to %s\n", &newboot[0x1f1]);
if (opts->kernel.stdbs)
printf("Boot sector kernel load segment set to %X:0h\n", opts->kernel.loadaddr);
else
printf("Boot sector kernel jmp address set to 70:%Xh\n", opts->kernel.loadaddr);
}
#ifdef DDEBUG
printf("\nNew Boot Sector:\n");
dump_sector(newboot);
#endif
if (opts->writeBS)
{
#ifdef DEBUG
printf("Writing new bootsector to drive %c:\n", opts->dstDrive + 'A');
#endif
/* write newboot to a drive */
if (MyAbsReadWrite(opts->dstDrive, 1, 0, newboot, 1) != 0)
{
printf("Can't write new boot sector to drive %c:\n", opts->dstDrive + 'A');
exit(1);
}
/* for FAT32, we need to update the backup copy as well */
/* unless user has asked us not to, eg for better dual boot support */
/* Note: assuming sectors 1-5 & 7-11 (FSINFO+additional boot code)
are properly setup by prior format and need no modification
[technically freespace, etc. should be updated]
*/
if ((fs == FAT32) && !opts->skipBakBSCopy)
{
bs32 = (struct bootsectortype32 *)newboot;
#ifdef DEBUG
printf("writing backup bootsector to sector %d\n", bs32->bsBackupBoot);
#endif
if (MyAbsReadWrite(opts->dstDrive, 1, bs32->bsBackupBoot, newboot, 1) != 0)
{
printf("Can't write backup boot sector to drive %c:\n", opts->dstDrive + 'A');
exit(1);
}
}
} /* if write boot sector to boot record*/
if (opts->bsFile != NULL)
{
if (opts->verbose)
printf("writing new bootsector to file %s\n", opts->bsFile);
saveBS(opts->bsFile, newboot);
} /* if write boot sector to file*/
reset_drive(opts->dstDrive);
/* unlock_drive */
generic_block_ioctl(opts->dstDrive + 1, 0x86a, NULL);
} /* put_boot */
/*
* Returns TRUE if `drive` has at least `bytes` free space, FALSE otherwise.
* put_sector() must have been already called to determine file system type.
*/
BOOL check_space(COUNT drive, ULONG bytes)
{
#ifdef WITHFAT32
if (fs == FAT32)
{
char *drivename = "A:\\";
drivename[0] = 'A' + drive;
getextdrivespace(drivename, &x, sizeof(x));
return x.xfs_freeclusters > (bytes / (x.xfs_clussize * x.xfs_secsize));
}
else
#endif
{
#ifdef __TURBOC__
struct dfree df;
getdfree(drive + 1, &df);
return (ULONG)df.df_avail * df.df_sclus * df.df_bsec >= bytes;
#else
struct _diskfree_t df;
_dos_getdiskfree(drive + 1, &df);
return (ULONG)df.avail_clusters * df.sectors_per_cluster
* df.bytes_per_sector >= bytes;
#endif
}
} /* check_space */
BYTE copybuffer[COPY_SIZE];
/* allocate memory from DOS, return 0 on success, nonzero otherwise */
int alloc_dos_mem(ULONG memsize, UWORD *theseg)
{
unsigned dseg;
#ifdef __TURBOC__
if (allocmem((unsigned)((memsize+15)>>4), &dseg)!=-1)
#else
if (_dos_allocmem((unsigned)((memsize+15)>>4), &dseg)!=0)
#endif
return -1; /* failed to allocate memory */
*theseg = (UWORD)dseg;
return 0; /* success */
}
#ifdef __TURBOC__
#define dos_freemem freemem
#else
#define dos_freemem _dos_freemem
#endif
/* copies file (path+filename specified by srcFile) to drive:\filename */
BOOL copy(SYSOptions *opts, const BYTE *source, COUNT drive, const BYTE * filename)
{
static BYTE src[SYS_MAXPATH];
static BYTE dest[SYS_MAXPATH];
unsigned ret;
int fdin, fdout;
ULONG copied = 0;
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
#if defined(__WATCOMC__) && __WATCOMC__ < 1280
unsigned short date, time;
#else
unsigned date, time;
#endif
#elif defined __TURBOC__
struct ftime ftime;
#endif
if (opts->verbose)
printf("Copying %s...\n", source);
truename(src, source);
sprintf(dest, "%c:\\%s", 'A' + drive, filename);
if (stricmp(src, dest) == 0)
{
if (opts->verbose)
printf("%s: source and destination are identical: skipping \"%s\"\n",
pgm, source);
return TRUE;
}
if ((fdin = open(source, O_RDONLY | O_BINARY)) < 0)
{
printf("%s: failed to open \"%s\"\n", pgm, source);
return FALSE;
}
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
_dos_getftime(fdin, &date, &time);
#elif defined __TURBOC__
getftime(fdin, &ftime);
#endif
if (!check_space(drive, filelength(fdin)))
{
printf("%s: Not enough space to transfer %s\n", pgm, filename);
close(fdin);
return FALSE;
}
if ((fdout =
open(dest, O_RDWR | O_TRUNC | O_CREAT | O_BINARY,
S_IREAD | S_IWRITE)) < 0)
{
printf(" %s: can't create\"%s\"\nDOS errnum %d\n", pgm, dest, errno);
close(fdin);
return FALSE;
}
#if 0 /* simple copy loop, read chunk then write chunk, repeat until all data copied */
while ((ret = read(fdin, copybuffer, COPY_SIZE)) > 0)
{
if (write(fdout, copybuffer, ret) != ret)
{
printf("Can't write %u bytes to %s\n", ret, dest);
close(fdout);
unlink(dest);
return FALSE;
}
copied += ret;
}
#else /* read in whole file, then write out whole file */
{
ULONG filesize;
UWORD theseg;
BYTE far *buffer; BYTE far *bufptr;
UWORD offs;
unsigned chunk_size;
/* get length of file to copy, then allocate enough memory for whole file */
filesize = filelength(fdin);
if (alloc_dos_mem(filesize, &theseg)!=0)
{
printf("Not enough memory to buffer %lu bytes for %s\n", filesize, source);
return FALSE;
}
bufptr = buffer = MK_FP(theseg, 0);
/* read in whole file, a chunk at a time; adjust size of last chunk to match remaining bytes */
chunk_size = (COPY_SIZE < filesize)?COPY_SIZE:(unsigned)filesize;
while ((ret = read(fdin, copybuffer, chunk_size)) > 0)
{
for (offs = 0; offs < ret; offs++)
{
*bufptr = copybuffer[offs];
bufptr++;
if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */
{
bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000);
}
}
/* keep track of how much read in, and only read in filesize bytes */
copied += ret;
chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied);
}
/* write out file, a chunk at a time; adjust size of last chunk to match remaining bytes */
bufptr = buffer;
copied = 0;
do
{
/* keep track of how much read in, and only read in filesize bytes */
chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied);
copied += chunk_size;
/* setup chunk of data to be written out */
for (offs = 0; offs < chunk_size; offs++)
{
copybuffer[offs] = *bufptr;
bufptr++;
if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */
{
bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000);
}
}
/* write the data to disk, abort on any error */
if (write(fdout, copybuffer, chunk_size) != chunk_size)
{
printf("Can't write %u bytes to %s\n", ret, dest);
close(fdout);
unlink(dest);
return FALSE;
}
} while (copied < filesize);
dos_freemem(theseg);
}
#endif
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
_dos_setftime(fdout, date, time);
#elif defined __TURBOC__
setftime(fdout, &ftime);
#endif
/* reduce disk swap on single drives, close file on drive last accessed 1st */
close(fdout);
#ifdef __SOME_OTHER_COMPILER__
{
#include <utime.h>
struct utimbuf utimb;
utimb.actime = /* access time */
utimb.modtime = fstatbuf.st_mtime; /* modification time */
utime(dest, &utimb);
};
#endif
/* and close input file, usually same drive as next action will access */
close(fdin);
if (opts->verbose)
printf("%lu Bytes transferred\n", copied);
return TRUE;
} /* copy */