/*************************************************************** 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 DRSYS */ /* SYS for Enhanced DR-DOS (OpenDOS enhancement Project) */ #define SYS_VERSION "v3.6e" #define SYS_NAME "FreeDOS System Installer " #ifdef DRSYS /* set displayed name & drop FD kernel config */ #undef SYS_NAME #define SYS_NAME "Enhanced DR-DOS System Installer " #ifdef FDCONFIG #undef FDCONFIG #endif #ifdef WITHOEMCOMPATBS #undef WITHOEMCOMPATBS #endif #endif #include #ifndef __GNUC__ #include #endif #include #ifndef __GNUC__ #include #include #ifdef __TURBOC__ #include #else #include #endif #endif #include #ifdef __TURBOC__ #include #endif #define SYS_MAXPATH 260 #include "portab.h" #include "algnbyte.h" #include "device.h" #include "dcb.h" #include "xstructs.h" #include "date.h" #include "../hdr/time.h" #include "fat.h" /* These definitions deliberately put here instead of * #including 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 #include #include #include #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;" "1:int $0x00; 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 #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, ®s, ®s); 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. */ #ifndef DRSYS /* FreeDOS */ FREEDOS_FILES #endif /* DR-DOS */ { "DRBIO.SYS", "DRDOS.SYS", 0x70/*:0*/, 1, 1 }, /* DR-DOS */ { "IBMBIO.COM", "IBMDOS.COM", 0x70/*:0*/, 1, 1 }, #ifdef DRSYS /* FreeDOS */ FREEDOS_FILES #endif #ifdef WITHOEMCOMPATBS /* PC-DOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x0, 0, 6138 }, /* pre v7 DR ??? */ /* 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 */ #ifndef DRSYS #define OEM_FD 0 /* standard FreeDOS mode */ #define OEM_EDR 1 /* use FreeDOS boot sector, but OEM names */ #define OEM_DR 2 /* FD boot sector,(Enhanced) DR-DOS names */ #else #define OEM_FD 2 /* standard FreeDOS mode */ #define OEM_EDR 0 /* use FreeDOS boot sector, but OEM names */ #define OEM_DR 1 /* FD boot sector,(Enhanced) DR-DOS names */ #endif #ifdef WITHOEMCOMPATBS #define OEM_PC 3 /* use PC-DOS compatible boot sector and names */ #define OEM_MS 4 /* use PC-DOS compatible BS with MS names */ #define OEM_W9x 5 /* use PC-DOS compatible BS with MS names */ #define OEM_RX 6 /* use PC-DOS compatible BS with Rx names */ #endif CONST char * msgDOS[DOSFLAVORS] = { /* order should match above items */ "\n", /* In standard FreeDOS/EnhancedDR mode, don't print anything special */ #ifndef DRSYS "Enhanced DR DOS (OpenDOS Enhancement Project) mode\n", #endif "DR DOS (OpenDOS Enhancement Project) mode\n", #ifdef DRSYS "\n", /* FreeDOS mode so don't print anything special */ #endif #ifdef WITHOEMCOMPATBS "PC-DOS compatibility mode\n", "MS-DOS compatibility mode\n", "Win9x DOS compatibility mode\n", "RxDOS compatibility 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 use FreeDOS compatible settings\n" " /OEM:EDR use Enhanced DR DOS 7+ compatible settings\n" " /OEM:DR use DR DOS 7+ compatible settings\n" #ifdef WITHOEMCOMPATBS " /OEM:PC use PC-DOS compatible settings\n" " /OEM:MS use MS-DOS compatible settings\n" " /OEM:W9x use MS Win9x DOS compatible settings\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, "EDR", 3) == 0) opts->flavor = OEM_EDR; else if (memicmp(argp, "DR", 2) == 0) opts->flavor = OEM_DR; #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 = #ifdef DRSYS OEM_EDR; #else OEM_FD; #endif if (opts->verbose) printf(msgDOS[opts->flavor]); /* 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"); 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, ®s, ®s); 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(®s, ®s); return regs.x.cflag; } /* fat32readwrite */ void reset_drive(int DosDrive) { union REGS regs; regs.h.ah = 0xd; intdos(®s, ®s); regs.h.ah = 0x32; regs.h.dl = DosDrive + 1; intdos(®s, ®s); } /* 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(®s, ®s); 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(®s, ®s, &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(®s, ®s, &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 >= 0xFFFfff5) { /* 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/sizeof(int)]; if (defaultload != 0x60 && defaultload != 0x70) { printf("%s: Internal error: FAT32 load seg unexpected content\n", pgm); exit(1); } ((int *)newboot)[0x78/sizeof(int)] = 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/sizeof(int)]; 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/sizeof(int)] = 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/sizeof(int)]; ((int *)newboot)[0x11A/sizeof(int)] = opts->kernel.loadaddr; } else { /* magic offset: jmp LOADSEG:xxxxh */ defaultload = ((int *)newboot)[0x119/sizeof(int)]; ((int *)newboot)[0x119/sizeof(int)] = 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; 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 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 */